设计模式(4):行为模式及结论
行为模式
行为模式涉及到算法和对象间职责的分配。行为模式不仅描述对象或类的模式,还描述它们之间的通信模式。
行为类模式使用继承机制在类之间分派行为。
行为对象模式使用对象复合而不是继承。
其它行为模式常将行为封装在一个对象中并将请求指派给它。
Chain Of Responsibility(职责链)——对象行为模式
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。
提交请求的对象并不明确地知道哪一个对象将会处理它——我们应该说请求有一个隐式的接受者
适用:
- 有多个对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定
- 你想在不明确指定接受者的情况下,向多个对象中的一个提交一个请求
- 可处理一个请求的对象集合应被动态指定
优缺点:
- 降低耦合度
- 增强了给对象指派职责的灵活性
- 不保证被接受
考虑的问题:
- 实现后继者链
- 定义新的链接
- 使用已有链接
- 连接后继者
- 表示请求
Command(命令)——对象行为型模式
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。动作(Action)、事物(Transaction)
使用:
- 抽象出待执行的动作以参数化某个对象
- 在不同时刻指定、排列和执行请求
- 支持取消操作
- 支持修改日志
- 用构建在原语操作上的高层操作构造一个系统
效果:
- Command模式将调用操作的对象与知道如何实现该操作的对象解耦
- Command是头等对象。可以行其他对象一样被操作和扩展
- 可以将多个命令封装成一个复合命令
- 增加新的Command很容易,因为这无需改变已有的类
考虑问题:
- 一个命令对象应达到何种智能程度
- 支持取消(undo)和重做(redo)
- 避免取消操作过程中错误积累
- 使用C++模板
Interpreter(解释器)——类行为型模式
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示解释语言中的句子
如果一种特定类型问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
效果最好的情况:
- 该文法简单对于复杂的文法,文法的类层次变得庞大而无法管理
- 效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式
优点和不足:
- 易于改变和扩展文法
- 也易于实现文法
- 发杂的文法难以维护
- 增加了新的解释表达式的方式
考虑的特殊问题:
- 创建抽象语法树
- 定义解释操作
- 与Flyweight共享终结符
Iterator(迭代器)——对象行为型模式
提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象内部表示——游标
将遍历机制与列表对象分离使我们可以定义不同的迭代器来实现不同的遍历策略,而无需在列表接口中列举它们。
适用:
- 访问一个聚合对象的内容而无需暴露它的内部表示
- 支持对聚合对象的多种遍历
- 为遍历不同的聚合结构提供统一的接口
作用:
- 它支持以不同的方式遍历一个聚合
- 迭代器简化了聚合的接口
- 在同一个聚合上可以由多个遍历
实现:
- 谁控制该迭代:由客户来控制迭代时,该迭代器称为一个外部迭代器,而当由迭代器控制迭代时,该迭代器称为内部迭代器
- 谁定义遍历算法
- 迭代器的健壮度如何
Mediator(中介者)——对象行为模型
用一个中介来封装一系列的对象交互。中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
面向对象鼓励将行为分布到各个对象中。这种分布可能会导致对象间有许多连接。在最坏的情况下,每一个对象都知道其他所有对象。
适用:
- 一组对象以定义良好但复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
- 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象
- 想定制一个分布在多个类中的行为,而又不想生成太多的子类
优缺点:
- 减少子类生成
- 它将各Colleague解耦
- 简化了对象协议
- 对对象如何协作进行了抽象
- 使控制集中化
Memento(备忘录)——对象行为模式
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保证的状态。——Token
一个备忘录是一个对象,它存储另一个对象在某个瞬间的内部状态,而后者称为备忘录的原发器。
适用:
- 必须保存一个对象在某一个时刻的(部分)状态,这样以后需要它才能恢复到先前的状态
- 如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性
效果:
- 保持封装边界
- 简化了原发器
- 使用备忘录可能代价很高
- 定义了窄接口和宽接口
- 维护备忘录的潜在代价
Observer(观察者)——对象行为型模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并被自动更新。
一个目标可以有任意数目的依赖它的观察者。一旦目标状态发生改变,所有观察者都得到通知。作为对这个通知的响应,每个观察者都将查询目标以使其状态与目标的状态同步。
适用:
- 当一个抽象模型有两个方面,一个方面依赖于另一个方面。将这二者封装在独立的对象中以使它们可以独自地改变和复用
- 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变
- 当一个对象必须通知其它对象,而他又不能假定其它对象是谁。
效果:
- 目标和观察者之间的抽象耦合
- 支持广播通信
- 意外的更新
实现:
- 创建目标到其观察者之间的映射关系
- 观察多个目标
- 谁触发更新
- 由目标对象的状态设定操作在改变目标对象的状态后自动调用Notify
- 让客户负责在适当的时候调用Notify
- 对以删除目标的悬挂引用
- 在发出通知前确保目标的状态自身是一致的
- 避免特定于观察者的更新协议——推/拉模型
- 显示的指定感兴趣的改变
- 封装复杂的更新语义
- 结合目标类和观察者类
State(状态)——对象行为型模式
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
适用:
- 一个对象的行为取决于它的状态,并且它必须运行在运行时刻根据状态改变它的行为
- 一个操作中含有庞大的多分支的条件语句,而且这些分支依赖于该对象的状态
效果:
- 它将与特定的状态相关的行为局部化,且将不同状态的行为分割开来
- 使得状态转换显示化
- State对象可被共享
实现:
- 谁定义状态转换
- 基于表的另一种方法
- 创建和销毁State对象
- 使用动态继承
Strategy(策略)——对象行为型模式
定义一系列的算法,把它们一个个封装起来,并且他们可以相互替换。本模式使得算法可独立于使用它的客户而变化
适用:
- 许多相关类仅仅是行为有异
- 需要使用一个算法的不同变体
- 算法使用客户不应该知道是数据
- 一个类定义了多种行为
优缺点:
- 相关算法系列
- 一个替代集成的的方法
- 消除了一些条件语句
- 实现的选择
- 客户必须了解不同的Strategy
- Strategy和Context之间的通信开销
- 增加了对象的数目
问题:
- 定义Strategy和Context接口
- 将Strategy作为模板参数
- 使Strategy对象称为可选的
Template Method(模板方法)——类行为模式
定义一个操作中的算法骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构既可以重定义该算法的某些特定步骤。
适用:
- 一次性实现一个算法的不变部分,并将可变的行为留给子类来实现
- 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复
- 控制子类扩展
模板方法调用以下类型的操作:
- 具体的操作
- 具体的AbstractClass的操作
- 原语操作
- Factory Method
- 钩子操作
Visitor(访问者)——对象行为模型
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
适用:
- 一个对象结构包含很多类对象
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作
- 定义对象结构的类很少改变
优缺点:
- 访问者模式使得易于增加新的操作
- 访问者集中相关的操作而分离无关的操作
- 增加新的ConcreteElement类很困难
- 通过类层次进行访问
- 积累状态
- 破坏封装
结论
面向对象软件的生命周期常分为几个阶段:原型阶段、扩展阶段和巩固阶段。
一旦软件进入青春期并交付使用,其演化就由以下两个互相冲突的要求来决定:
- 该软件必须满足更多的需求
- 该软件必须更易于复用
以一种松散的方式把一些设计模式串接在一起来造建筑是可能的。这样的建筑仅仅是一些模式的堆砌而不紧凑。这不够深刻。然而另有一种组合模式的方式,许多模块重叠在同一个物理空间里:这样的建筑非常紧凑,在一小块空间里集成了许多内涵;由于这种紧凑,它变得深刻。
Author: moyu-x
Link: http://moyu-x.com/2017/01/02/201701/DesignPattern-4/
License: 知识共享署名-非商业性使用 4.0 国际许可协议