设计模式 -- 行为模式
5 章行为模式
涉及到算法和对象间职责的分配,不仅描述对象或类的模式,还描述他们之间的通信模式
- 职责链模式
- 命令模式
- 解释器模式
- 迭代器模式
- 中介者模式
- 观察者模式
- 状态模式
- 策略模式
- 模板方法模式
- 访问者对象
CHAIN OF RESPONSIBILITY(职责链) –对象行为型模式
意图:就是一个行为沿着一个对象链冒泡,可能会被其中的某个对象处理,使得多个对象都有机会处理对象,避免了发送者和处理者之间的耦合关系。
适用性:
- 多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定
- 不确定接受者的时候,向多个对象中的一个提交一个请求
结构:就是很多个处理者,他们都拥有处理请求的方法并且可以访问到他们的上一级,一个请求从底层向上传递,可能会被其中的一个处理了或者继续向上传递。
效果:
- 降低耦合性:对象无需知道谁来处理,仅仅需要知道这个请求被正确的处理了,接受者和发送者都没有对方的明确信息
- 不保证被接受
- 增强了给对象指派职责的灵活性
总结:如果这个链经常被修改的话,是可以弄成这个模式的,不一定那种 if else 的就一定合适,主要还是看项目的演化方向
COMMAND(命令) –对象行为型模式
意图:将一个请求封装成一个对象,可以使用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作
适用性:
- 在不同的时刻指定,排列和执行请求
- 支持取消操作,就是 command 的 execute 可以顺便存储下状态,然后在取消操作时按照这个状态消除掉操作的影响。
- 支持修改日志,就是借口提供了添加装载操作以及存储操作,崩溃回复的时候可以拿取这些命令并且重新执行他们。
结构:就是客户端声明一些实现的命令对象,并且给他指定好接受者对象。然后发起者拥有这些命令对象。真正的触发由发起者来发起。发起者可以对拥有的这些个命令进行各种排序啊,状态的存储啊等等操作。
总结:就是将一个命令的直接执行过程拆开,分成了发起者和执行者两个角色。然后用一个命令对象来管理他们的关系。通过这个命令对象我们可以存储一些状态,通过这些个状态,我们可以一个是松耦合,一个是支持排队,取消,以及修改以及日志。
效果:
- 将调用操作的对象以及知道如何实现操作的对象解耦
- 增加新的命令很容易,因为不用改变已有的类
- 可以将多个命令组装成一个复合命令
INTERPRETER(解释器) –类行为型模式
意图:给定一个语言,定义它的文法的表示,并定义一个解释器。解释器使用该表示来解释句子。
适用性:
- 当有语言需要解释执行,并且可以将语言中的句子表示为抽象语法树的时候
结构:其实主要是在 context 里面有解析整个语句的过程,会使用一个栈,这个栈里会一步步的解析,最后的结果是各个解释的类的结果(其实就是生成一个抽象语法树)。最后的结果是一个嵌套了很多解释器的结果。栈其实就只有一个内容。每个解释类实现不同,但是都有同一个方法,基本会有两种解释器,一种就是值,一种就是操作。
效果:
- 易于改变和扩展文法
- 易于实现文法
- 复杂的文法难以维护。因为每一条规则至少定义了一个类
总结:使用的情况还真的很少,毕竟很少人会来写一个语法。最后就是通过遍历来生成一个抽象的语法树。
ITERATOR(迭代器) –对象行为型模式
意图: 提供一个方法顺序访问一个聚合对象的各个元素,而又不需要暴露内部表示。
适用性:
- 访问一个聚合对象的内部而不暴露他的内部展示
- 支持对聚合对象的多种遍历
- 为遍历不同的集合对象提供一个统一的接口
结构:这个集合对象也是一个抽象的接口,也会有一个实现的集合对象。然后申明了一个抽象的迭代器,然后一个具体实现的迭代器(new 的时候会传入一个具体的集合对象,这里切换迭代器其实是一种工厂模式)。两个抽象主要是为了遍历不同的结构和不同的遍历方法。
效果:
- 支持不同的方式遍历一个集合
- 简化了聚合的接口
- 在同一个聚合上可以有多个遍历
MEDIATOR(中介者) –对象行为型模式
意图:用一个中介对象来封装一系列的对象交互,使得各对象不需要显示的相互引用。从而松散耦合。而且可以独立的改变他们之间的交互。
适用性:
- 一组对象以定义良好但是复杂的方式通信,相互依赖的结构混乱且难以理解
- 一个对象引用很多对象并且直接与这些对象通信,导致难以复用这个对象
- 想定制一个分布在多个类之间的行为。而不像生成太多的子类
结构:就是定义了一个同事的抽象,然后每个实现的子类的实例拥有中介者的引用。每个指令发出去都是通过中介者来发。中介者拥有着这些个实例的所有的引用。他会针对发出的请求的实例做不同的事情。
效果:
- 减少了子类生成
- 将各个 colleague 解耦
- 简化了对象协议,把多对多的关系,变成了中介者与各个实例间一对多的关系
- 使得控制集中化,但是中介者本身可能会变得无法维护
MEMENTO(备忘录) –对象行为型模式
意图:不破坏封装性的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态以便恢复。
适用性:
- 必须保存一个对象在某个时刻的状态,以后需要他的时候才能恢复到它的状态。
- 如果用接口让其他对象能访问到这些状态,会暴露对象的实现细节并破坏对象的封装性
结构:就是发起者负责根据自身状态创建备忘录对象(他可以通过备忘录来进行自身的恢复工作)。然后外部还有个负责人来保存这个备忘录的引用,提供 get,set 方法。发起者再从这个负责人这里拿取来恢复。
效果:
- 保持封装边界
- 简化了原发器
- 使用备忘录的代价可能会比较高
- 维护备忘录的代价可能不小
OBSERVER(观察者) –对象行为型模式
意图:定义对象之间的一对多的依赖关系,当一个对象状态发生改变的时候,依赖的对象得到通知并自动更新
适用性:
- 当一个对象的改变需要通知多个对象,而且不知道具体有多少
- 不希望对象之间的紧耦合
结构:就是一个抽象对象类,里面有添加和删除观察者的方法,还有通知的方法。具体的对象实现这个类,然后保存有自己内部的状态。有个观察者的抽象,里面只提供了 update 方法,然后具体的观察者实现抽象类并存有内部的状态。
效果:
- 目标和观察者之间的解耦(这种解耦一般是由于处在不同的层级上,所以需要解耦)
- 支持广播通信
- 可能导致意外的更新
总结:其实我们 js 中大部分的观察者都是推模式,但是真正的观察者其实是拉模式。
STATE(状态) –对象行为型模式
意图:允许一个对象在他的内部状态改变时改变他的行为。
适用性:
- 当一个对象的行为取决于他的状态。并且会在运行时刻根据状态改变它的行为。
- 拥有庞大的多分支的条件语句。且这些分支依赖于该对象的状态。
结构:就是一个 context 内部聚合了一个 state 的实例。state 定义了 context 的特定的状态的行为。注意每个实现了 state 的类都要把这些个行为实现。然后通过切换状态然后重新调用方法来实现。
效果:
- 将与特定状态相关的行为局部化
- 使得状态转化显示化
- state 状态可以被共享。这个点和享元模式就有点关系了。
总结:这个模式其实和职责链有些相似,当初我也觉得职责链能够解决多个 if 的场景,后来发现状态模式更适合。因为状态是单个对象的状态的切换,而职责链是多个对象之间的切换。而且职责链其实是 switch。而状态是 if else。这个模式和享元可以配合。因为他的 state 是独立的。注意状态这个概念是持久化的,才能叫状态。必须是严格的显示依赖于状态才行。
STRATEGY(策略) –对象行为型模式
意图:定义一系列的算法,把他们一个个封装起来,使得他们可以相互替换。使得算法可独立于使用它的客户而变化。
适用性:
- 许多相关的类仅仅是行为有异
- 需要使用一个算法的不同变体
- 算法使用用户不应该知道的数据
结构:很简单,就是一个上下文聚合了一个抽象的算法。这个算法会有很多种实现。
效果:
- 定义了一系列的可供重用的算法或者行为
- 替换了继承的能力
- 消除了一些条件语句
总结:这个玩意和桥接模式很像,但是区别还是有的,首先策略是行为模式,而桥接是结构性模式。桥接的的调用方是可以有自己的变化的,而策略模式不能有自己的变化。桥接模式更多的是体系的隔离。可以说桥接模式包含着策略模式。但是桥接模式比策略更加的高级一些。更多的是一种接口隔离的原则,使之可以松散的耦合。主要是看一个问题的角度问题
TEMPLATE METHOD(模板方法) –类行为型模式
意图:定义一个操作算法的骨架,将一些步骤延迟到子类。是的不改变算法的结构就可以重新定义特定步骤
适用性:
- 一次性实现不变的部分,将可变的行为留给子类来实现
- 各子类中公共行为被提取出来集中到公共父类来避免代码重复
- 控制子类扩展(就是只提供了特定的口子来让子类扩展)
结构:就是声明一个模板类,大部分的结构都是已经实现好了的,一些特定的步骤用虚方法来写,然后子类其实只要实现虚方法就好了
效果:
- 模板方法提供一个反向的控制结构,通过一个父类来调用一个子类的操作。这点很神奇啊(钩子操作)
- 提取了公共行为来代码复用。
总结:其实模板方法的运用就是提供了钩子函数,在定义了具体的执行顺序(也就是模板)的时候,对外提供了在特定的点执行的能力的钩子函数。
VISITOR(访问者) –对象行为型模式
意图:表示一个作用于对象结构的个元素的操作。使得可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
适用性:
- 一个对象结构包含很多类对象。包含了不同的接口,想对这些对象实施依赖于具体类的操作
- 对一个对象结构进行很多不同的且不相关的操作。想避免这些操作污染对象的类。
- 定义对象结构的类很少改变,但是需要在这个结构上定义新的操作。
结构:就是一个对象结构内部有多个对象。访问内部的这些对象的时候。传入一个访问者对象。然后调用特定的方法的时候,把自身传进去。然后在访问者对象里面根据传入的是谁来决定相应的操作
效果:
- 访问者模式易于增加新的操作
- 集中相关的操作而分离无关的操作
- 通过类层次进行访问
- 可能会破坏封装,因为对外提供了内部状态的公共操作
总结
其实行为模式主要就是在封装变化,将消息的发送者和接受者进行解耦。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 981909093@qq.com
文章标题:设计模式 -- 行为模式
文章字数:3.5k
本文作者:泽鹿
发布时间:2019-08-28, 16:45:23
最后更新:2019-08-28, 16:45:23
原始链接:http://panyifei.github.io/2019/08/28/读书笔记/设计模式/5章行为模式/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。