Design Pattern (3) 设计模式(3) Institute of Computer Software 2018/11/8 Nanjing University 2018/11/8
摘要 Design Patterns Why, What, How Creational, Structural and Behavioral Patterns Institute of Computer Software Nanjing University 2018/11/8
Behavioral Patterns 行为模式是对在不同的对象之间划分责任和算 法的抽象化。行为模式不仅仅是关于类和对象 的,而且关注它们之间的通信模式。 类的行为模式:使用继承关系在几个类之间分配行 为 – Interpreter, Template Method 对象的行为模式:使用对象的聚合来分配行为 Institute of Computer Software Nanjing University 2018/11/8
Behavioral Patterns Chain of Responsibility Command Interpreter Iterator Mediator Memento Observer State Strategy Template Method Visitor Institute of Computer Software Nanjing University 2018/11/8
Chain of Responsibility Intent 使多个对象都有机会处理请求,从而避免请求的发送者和接 收者之间的耦合关系。将这些对象连成一条链,并沿着这条 链传递该请求,直到有一个对象处理它为止。 Motivation 应根据普遍性即从最特殊到最普遍的顺序来组织帮助信息, 比如用户界面中会有一个对象来处理帮助请求,但是哪一个 对象则取决于上下文 给多个对象处理一个请求的机会,从而解耦发送者和接受者 Institute of Computer Software Nanjing University 2018/11/8
Example Institute of Computer Software Nanjing University 2018/11/8
Applicability 有多个对象可以处理一个请求,哪个对象处理 该请求则在运行时刻确定 在不明确指定接收者的情况下,向多个对象中 的一个提交一个请求 可处理一个请求的对象集合应被动态制定 Institute of Computer Software Nanjing University 2018/11/8
Structure Institute of Computer Software Nanjing University 2018/11/8
Participants Handler ConcreteHandler Client 定义一个处理请求的接口 (可选)实现后继链 处理它所负责的请求 可访问它的后继者 如果可处理该请求,就处理之;否则将该请求转发给它的后 继者 Client 提交请求 Institute of Computer Software Nanjing University 2018/11/8
Collaboration 当Client提交一个请求时,请求沿链传递直至有 一个ConcreteHandler对象负责处理它 Sample Code Institute of Computer Software Nanjing University 2018/11/8
Consequences 优点 缺点 降低耦合度:对象仅需知道请求会被“正确”地处理。 接收者和发送者都没有对方的明确信息 增强了给对象指派职责的灵活性 缺点 不保证被接受 Institute of Computer Software Nanjing University 2018/11/8
Implementation 实现后继者链 连接后继者:如果没有已有的引用可定义一个 链,那么你必须自己引入它们 定义新的链接 使用已有的链接 连接后继者:如果没有已有的引用可定义一个 链,那么你必须自己引入它们 表示请求:最简单的方式是hard-coded操作调用 Institute of Computer Software Nanjing University 2018/11/8
Related Patterns 经常与Composite一起使用。这时,一个构件的 父构件可作为它的后继。 Institute of Computer Software Nanjing University 2018/11/8
Known Use:Event Bubbling Java AWT 1.0 思考题:有何缺点? public boolean action(Event event, Object obj){ if (event.target == btnOK){ doOkBtnAction();} else if (event.target == btnExit) { doExitBtnAction();} else { return super.action(event.obj);} return true; } DHTML的事件处理 Event_Bubbling.html Institute of Computer Software Nanjing University 2018/11/8
Command Aliases: Action,Transaction Intent Motivation 将一个请求封装为一个对象,从而使你可用不同的请求对客 户进行参数化;对请求排队或记录请求日志,以及支持可撤 销的操作 Motivation 把请求信息和请求执行过程封装起来 往往需要把命令请求与处理请求的对象分开,command模式 可以把调用操作的对象与操作的目标对象分开 允许通过多种途径调用同一个请求——请求的重用 Institute of Computer Software Nanjing University 2018/11/8
Example The "check" at a diner Institute of Computer Software Nanjing University 2018/11/8
Example invoker client command configuration Institute of Computer Software Nanjing University 2018/11/8
Example receiver Institute of Computer Software 2018/11/8 Nanjing University 2018/11/8
Example receiver Institute of Computer Software 2018/11/8 Nanjing University 2018/11/8
Example 一系列的命令 Institute of Computer Software 2018/11/8 Nanjing University 2018/11/8
Applicability 抽象出待执行的动作以参数化某对象,可以代 替“回调”函数 在不同的时刻指定、排列和执行请求 支持取消操作。Execute操作可以在实施前将状 态存储起来 支持修改日志,这样当系统崩溃时,这些修改 可以被重做一遍 用构建在原语操作上的高层操作构造一个系统, 支持“事务” Institute of Computer Software Nanjing University 2018/11/8
Structure Institute of Computer Software Nanjing University 2018/11/8
Participants Command ConcreteCommand Client Invoker Receiver 声明执行操作的接口 将一个接收者对象绑定于一个动作 调用接收者相应的操作以实现Execute Client 创建一个具体命令对象并设定它的接收者 Invoker 要求该命令执行这个请求 Receiver 知道如何实施与执行一个请求相关的操作。任何类都可能作 为一个接收者 Institute of Computer Software Nanjing University 2018/11/8
Collaboration Sample Code Decouple anInvoker and aReceiver by aCommand Institute of Computer Software Nanjing University 2018/11/8
Consequences 将调用操作的对象与知道如何实现该操作的对象解耦 Command是first class object。它们可像其它对象一样 被操纵和扩展 可将多个Command装配成一个复合Command,一般说 来,复合Command是Composite模式的一个实例 增加新的Command很容易,无需改变已有的类 缺点:会导致某些系统有过多的具体命令类 Institute of Computer Software Nanjing University 2018/11/8
Implementation 使用C++模板:针对不能取消且不需要参数的 命令 How intelligent should a command be? 仅确定一个接收者和执行该请求的动作 自己实现所有功能,不需要额外的接收者 支持undo和redo:ConcreteCommand需要存储额外 的状态信息(思考题:需要存储哪些状态?单级 和多级的undo和redo分别如何实现?) 避免undo操作中的错误积累,有必要存入更多的 信息以保证能精确复原。 使用C++模板:针对不能取消且不需要参数的 命令 Institute of Computer Software Nanjing University 2018/11/8
Related Patterns Composite模式可用来实现command组合 为实现undo/redo,可以用其他行为模式来管理状 态,如Memento模式。Command被放到history list 之前,可以用Prototype模式复制自身 Institute of Computer Software Nanjing University 2018/11/8
Interpreter Intent Motivation 给定一个语言,定义它的文法的一种表示,并定义 一个解释器,这个解释器使用该表示来解释语言中 的句子。 Motivation 如果一种特定类型的问题发生的频率足够高, 那么 可能就值得将该问题的各个实例表述为一个简单语 言中的句子。这样就可以构建一个解释器, 该解释 器通过解释这些句子来解决该问题。解释器模式描 述了如何为简单的语言定义一个文法并解释。 Institute of Computer Software Nanjing University 2018/11/8
Example expression ::= literal | alternation | sequence | repetition | '(' expression ')‘ alternation ::= expression '|' expression sequence ::= expression '&' expression repetition ::= expression '*' literal ::= 'a' | 'b' | 'c' | ... { 'a' | 'b' | 'c' | ... }* Institute of Computer Software Nanjing University 2018/11/8
Example Institute of Computer Software Nanjing University 2018/11/8
Example raining & (dogs | cats) * 抽象语法树 Institute of Computer Software Nanjing University 2018/11/8
Applicability 当有一个语言需要解释执行, 并且你可将该语言中的 句子表示为一个抽象语法树时,可使用解释器模式。 而当存在以下情况时该模式效果最好: 该文法简单. 对于复杂的文法, 文法的类层次变得庞大而无法管理。此时 语法分析程序生成器这样的工具是更好的选择。它们无需构 建抽象语法树即可解释表达式, 这样可以节省空间而且还可 能节省时间。 效率不是一个关键问题. 最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它们转换成另一种形式。例如,正则表达式通常 被转换成状态机。但即使在这种情况下, 转换器仍可用解释 器模式实现, 该模式仍是有用的。 Institute of Computer Software Nanjing University 2018/11/8
Structure Institute of Computer Software Nanjing University 2018/11/8
Participants Client AbstractExpression 声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享 TerminalExpression 实现与文法中的终结符相关联的解释操作 一个句子中的每一个终结符需要该类的一个实例 NonterminalExpression 对文法中的每一条规则R ::= R1R2. . . Rn都需要一个NonterminalExpression类 为从R1到Rn的每个符号都维护一个AbstractExpression类型的实例变量 为文法中的非终结符实现Interpret操作,一般要递归地调用表示R1到Rn的 那些对象的解释操作 Context 包含解释器之外的一些全局信息 Client 建造一个语法抽象树;调用interpret() Institute of Computer Software Nanjing University 2018/11/8
Collaboration Client构建(或被给定)一个句子, 它是 NonterminalExpression和TerminalExpression的实例 的一个抽象语法树. 然后初始化上下文并调用解 释操作。 每一非终结符表达式节点定义相应子表达式的 解释操作。而各终结符表达式的解释操作构成 了递归的基础。 每一节点的解释操作用上下文来存储和访问解 释器的状态。 Institute of Computer Software Nanjing University 2018/11/8
Consequences 易于改变和扩展文法 也易于实现文法 复杂的文法难以维护 增加了新的解释表达式的方式 Institute of Computer Software Nanjing University 2018/11/8
Implementation 创建抽象语法树 定义解释操作 与Flyweight模式共享终结符 Sample Code Institute of Computer Software Nanjing University 2018/11/8
Related Patterns Composite:抽象语法树是一个复合模式的实例 Flyweight:说明了如何在抽象语法树中共享终 结符 Iterator:解释器可用一个迭代器遍历该结构 Visitor:可用来在一个类中维护抽象语法树中的 各节点的行为 Institute of Computer Software Nanjing University 2018/11/8
Iterator Aliases:Cursor Intent Motivation 提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。 Motivation 将对聚合对象的访问和遍历从聚合对象中分离出来 并放入一个迭代器(iterator) 将遍历机制与聚合对象分离使我们可以定义不同的 迭代器来实现不同的遍历策略,而无需在聚合接口 中列举它们 Institute of Computer Software Nanjing University 2018/11/8
Example Java Iterator Interface Institute of Computer Software public interface Iterator<E>{ boolean hasNext(); <E> next(); void remove(); } Institute of Computer Software Nanjing University 2018/11/8
Polymorphic Iterator Institute of Computer Software 2018/11/8 Nanjing University 2018/11/8
Applicability 访问一个聚合对象的内容而无需暴露它的内部 表示。 支持对聚合对象的多种遍历。 为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。 Institute of Computer Software Nanjing University 2018/11/8
Structure Institute of Computer Software Nanjing University 2018/11/8
Participants Iterator ConcreteIterator Aggregate ConcreteAggregate 迭代器定义访问和遍历元素的接口 ConcreteIterator 具体迭代器实现迭代器接口 对该聚合遍历时跟踪当前位置 Aggregate 聚合定义创建相应迭代器对象的接口 ConcreteAggregate 具体聚合实现创建相应迭代器的接口,该操作返回 ConcreteIterator的一个适当的实例。 Institute of Computer Software Nanjing University 2018/11/8
Collaboration ConcreteIterator跟踪聚合中的当前对象,并能够 计算出待遍历的后继对象 Institute of Computer Software Nanjing University 2018/11/8
Consequences 它支持以不同的方式遍历一个聚合,复杂的聚 合可用多种方式进行遍历 迭代器简化了聚合的接口,有了迭代器的遍历 接口,聚合本身就不再需要类似的遍历接口了 在同一个聚合上可以有多个遍历,每个迭代器 保持它自己的遍历状态。因此你可以同时进行 多个遍历。 缺点:给客户端聚集被顺序化的错觉;迭代子 给出的聚集元素没有类型特征 Institute of Computer Software Nanjing University 2018/11/8
Implementation 实现很灵活,需要根据语言的控制结构进行权 衡 谁控制迭代?谁定义遍历算法? 迭代器健壮程度如何? 附加的迭代器操作 多态的迭代器? 迭代器可有特权访问 用于复合对象的迭代器,空迭代器 Institute of Computer Software Nanjing University 2018/11/8
不同的实现 宽接口 VS. 窄接口 宽接口:一个聚集的接口提供了可以用来修改聚集 元素的方法 窄接口:一个聚集的接口没有提供修改聚集元素的 方法 Institute of Computer Software Nanjing University 2018/11/8
白箱聚集 VS. 黑箱聚集 白箱聚集:聚集对象为所有对象提供同一个接 口(宽接口) 思考题:此类迭代子的意义 Sample Code 迭代子可以从外部控制聚集元素的迭代,控制的仅 仅是一个游标—游标(Cursor)/外禀(Extrinsic)迭代子 抽象聚集 具体聚集 具体迭代子 抽象迭代子 客户端 -宽接口 思考题:此类迭代子的意义 Sample Code Institute of Computer Software Nanjing University 2018/11/8
白箱聚集 VS. 黑箱聚集 黑箱聚集:聚集对象为迭代子对象提供一个宽 接口,而为其它对象提供一个窄接口。同时保 证聚集对象的封装和迭代子功能的实现。 迭代子是聚集的内部类,可以自由访问聚集的元素。 迭代子可以自行实现迭代功能并控制聚集元素的迭 代逻辑—内禀迭代子(Intrinsic Iterator) -窄接口 抽象聚集 客户端 抽象迭代子 -宽接口 具体聚集 具体迭代子 Sample Code Institute of Computer Software Nanjing University 2018/11/8
其它考虑 主动(Active)迭代子 vs. 被动(Passive)迭代子 静态迭代子 vs. 动态迭代子 主动:由客户调用next()等迭代方法 被动:迭代子自行推进遍历过程 静态迭代子 vs. 动态迭代子 静态:由聚集对象创建并持有聚集对象的快照,在 产生后这个快照的内容不再变化 动态:迭代子保持对聚集元素的引用,任何对聚集 内容的修改都会反映到迭代子对象上 过滤迭代子:扫过聚集元素的同时进行计算 Institute of Computer Software Nanjing University 2018/11/8
Related Patterns Composite:Iterator常被应用到复合的递归结构 上 Factory Method:多态迭代器 Memento:常用Memento来捕获一个迭代的状态 延伸阅读:Java Collections Framework Institute of Computer Software Nanjing University 2018/11/8
Mediator Intent Motivation 用一个中介对象来封装一系列的对象交互。中介者使各对象 不需要显式地相互引用,从而使其耦合松散,而且可以独立 地改变它们之间的交互。 Motivation 虽然将一个系统分割成许多对象通常可以增强可复用性, 但 是对象间相互连接的激增又会降低其可复用性。 可以通过将集体行为封装在一个单独的mediator对象中以避 免这个问题。中介者负责控制和协调一组对象间的交互。中 介者充当一个中介以使组中的对象不再相互显式引用。这些 对象仅知道中介者, 从而减少了相互连接的数目。 Institute of Computer Software Nanjing University 2018/11/8
Example Institute of Computer Software Nanjing University 2018/11/8
Example colleague mediator colleague colleague Institute of Computer Software Nanjing University 2018/11/8
Applicability 一组对象以定义良好但是复杂的方式进行通信。 产生的相互依赖关系结构混乱且难以理解。 一个对象引用其他很多对象并且直接与这些对 象通信,导致难以复用该对象。 想定制一个分布在多个类中的行为,而又不想 生成太多的子类。 Institute of Computer Software Nanjing University 2018/11/8
Structure Institute of Computer Software Nanjing University 2018/11/8
Participants Mediator ConcreteMediator Colleague 具体中介者通过协调各同事对象实现协作行为。 了解并维护它的各个同事 Colleague 每一个同事类都知道它的中介者对象 每一个同事对象在需与其他的同事通信的时候,与 它的中介者通信 Institute of Computer Software Nanjing University 2018/11/8
Collaboration 同事向一个中介者对象发送和接收请求。中介 者在各同事间适当地转发请求以实现协作行为 Sample Code Institute of Computer Software Nanjing University 2018/11/8
Consequences 减少了子类生成:将分布于多个对象间的行为 集中在一起 它将各Colleague解耦 它简化了对象协议:多对多一对多 它对对象如何协作进行了抽象 它使控制集中化,中介者模式将交互的复杂性 变为中介者的复杂性;中介类可能难以复用 Institute of Computer Software Nanjing University 2018/11/8
Implementation 忽略抽象的Mediator类,当各Colleague仅与一 个Mediator一起工作时,没有必要定义一个抽象 的Mediator类 Colleague—Mediator通信,可以采用Observer模 式,或者在Mediator中定义一个特殊的通知接口, 各Colleague在通信时直接调用该接口 Institute of Computer Software Nanjing University 2018/11/8
Related Patterns Facade是对一个对象子系统的抽象,从而提供 了一个更为方便的接口,它的协议是单向的; Mediator提供了各Colleague对象不支持或不能 支持的协作行为,而且协议是多向的 Colleague可以使用Observer模式与Mediator通信 Institute of Computer Software Nanjing University 2018/11/8
Memento Aliases:Token,Snapshot Intent Motivation 在不破坏封装性的前提下,捕获一个对象的内部状态,并在 该对象之外保存这个状态。这样以后就可将该对象恢复到原 先保存的状态。 Motivation 一个memento是一个对象, 它存储另一个对象在某个瞬间的内 部状态,而后者称为备忘录的原发器(originator)。当需要设 置原发器的检查点(checkpoint)时, 取消操作机制会向原发器 请求一个备忘录。原发器用描述当前状态的信息初始化该备 忘录。只有原发器可以向备忘录中存取信息,备忘录对其他 的对象“不可见”。 Institute of Computer Software Nanjing University 2018/11/8
Example ConstraintSolver Undo? Institute of Computer Software Nanjing University 2018/11/8
Applicability 必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态 必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态 如果一个用接口来让其它对象直接得到这些状 态,将会暴露对象的实现细节并破坏对象的封 装性 Institute of Computer Software Nanjing University 2018/11/8
Structure -wide -narrow Institute of Computer Software 2018/11/8 Nanjing University 2018/11/8
Participants Memento Originator Caretaker 备忘录存储原发器对象的内部状态。原发器根据需要决定备忘录 存储原发器的哪些内部状态。 防止原发器以外的其他对象访问备忘录。备忘录实际上有两个接 口,管理者(caretaker) 只能看到备忘录的窄接口—它只能将备忘录 传递给其他对象。相反, 原发器能够看到一个宽接口, 允许它访问 返回到先前状态所需的所有数据。理想的情况是只允许生成本备 忘录的那个原发器访问本备忘录的内部状态。 Originator 原发器创建一个备忘录,用以记录当前时刻它的内部状态。 使用备忘录恢复内部状态。 Caretaker 负责保存好备忘录 不能对备忘录的内容进行操作或检查 Institute of Computer Software Nanjing University 2018/11/8
Collaboration 管理器向原发器请求一个备忘录, 保留一段时间 后,将其送回给原发器 备忘录是被动的。只有创建备忘录的原发器会 对它的状态进行赋值和检索 Institute of Computer Software Nanjing University 2018/11/8
Consequences 保持封装边界,该模式把可能很复杂的 Originator内部信息对其他对象屏蔽起来 它简化了Originator,让客户管理它们请求的状 态将会简化Originator,并且使得客户工作结束 时无需通知Originator 使用备忘录可能代价很高 定义窄接口和宽接口:有些语言可能难以保证 只有原发器可以访问备忘录的状态 维护备忘录的潜在代价 Institute of Computer Software Nanjing University 2018/11/8
Implementation 语言支持 宽接口和窄接口的不同实现方法 存储增量式改变:如果备忘录的创建及其返回 (给它们的原发器)的顺序是可预测的,备忘 录可以仅存储原发器内部状态的增量改变 Institute of Computer Software Nanjing University 2018/11/8
白箱实现 宽接口和宽接口 优点:简单 缺点:破坏对originator状态的封装 Sample Code Institute of Computer Software Nanjing University 2018/11/8
黑箱实现 宽接口和窄接口– 双重接口 Sample Code C++:将Memento的接口设为私有,同时将 Originator设为Memento的友类 Java:内部成员类,且所有方法设置为私有 class Memento{ public: virtual ~Memento(); private: friend cladd Originator; Memento(); void setState(State *); State * getState(); … Sample Code Institute of Computer Software Nanjing University 2018/11/8
Related Patterns Command:命令可使用备忘录来为可撤销的操 作维护状态 Iterator:当备忘录模式支持多个checkpoints时, 在各个checkpoints之间进行遍历可用迭代模式 Institute of Computer Software Nanjing University 2018/11/8
Observer Aliases :Dependents, Publish-Subscribe Intent Motivation 定义对象间的一种一对多的依赖关系,当一个对象 的状态发生改变时, 所有依赖于它的对象都得到通 知并被自动更新。 Motivation 把系统分成一些相互关联的类或者对象,如何维护 这些类的实例一致性?-- 不希望为了维护一致性而 使各类紧密耦合 Institute of Computer Software Nanjing University 2018/11/8
Observer Motivation 这一模式中的关键对象是目标(subject)和观察者 (observer)。一个目标可以有任意数目的依赖它的观 察者。一旦目标的状态发生改变, 所有的观察者都 得到通知。作为对这个通知的响应,每个观察者都 将查询目标以使其状态与目标的状态同步。 这种交互也称为发布-订阅(publish-subscribe) 。目 标是通知的发布者。它发出通知时并不需知道谁是 它的观察者。可以有任意数目的观察者订阅并接收 通知。 Institute of Computer Software Nanjing University 2018/11/8
Example 最著名的应用:MVC Institute of Computer Software 2018/11/8 Nanjing University 2018/11/8
Applicability 当一个抽象模型有两个方面, 其中一个方面依赖 于另一方面。将这二者封装在独立的对象中以 使它们可以各自独立地改变和复用。 当对一个对象的改变需要同时改变其它对象, 而 不知道具体有多少对象有待改变。 当一个对象必须通知其它对象,而它又不能假 定其它对象是谁。换言之, 你不希望这些对象是 紧密耦合的。 Institute of Computer Software Nanjing University 2018/11/8
Structure Institute of Computer Software Nanjing University 2018/11/8
Participants Subject(目标) Observer(观察者) ConcreteSubject(具体目标) 目标知道它的观察者。可以有任意多个观察者观察同一个目标。 提供注册和删除观察者对象的接口。 Observer(观察者) 为那些在目标发生改变时需获得通知的对象定义一个更新接口。 ConcreteSubject(具体目标) 将有关状态存入各ConcreteObserver对象。 当它的状态发生改变时, 向它的各个观察者发出通知。 ConcreteObserver(具体观察者) 维护一个指向ConcreteSubject对象的引用。 存储有关状态,这些状态应与目标的状态保持一致。 实现Observer的更新接口以使自身状态与目标的状态保持一致。 Institute of Computer Software Nanjing University 2018/11/8
Collaboration Sample Code 当C o n c r e t e S u b j e c t发生任何可能导致其观察者与其本身状态不一致的改变时,它将通知 它的各个观察者。 在得到一个具体目标的改变通知后, ConcreteObserver 对象可向目标对象查询信息。 C o n c r e t e O b s e r v e r使用这些信息以使它的状态与目标对象的状态一致。 注意:发出改变请求的Observer对象并不立即更新,而是将其推迟到它从目标得到一个通知之后。Notify不总是由目标对象调用。它也可被一个观察者或其它对象调用 Sample Code Institute of Computer Software Nanjing University 2018/11/8
Consequences Subject和Observers的抽象耦合:Subject只知道 它有一系列的Observers,每个符合抽象的 Observer类的简单接口,但是不知道具体类型 支持广播通信:Subject对象不关心到底有多少 对象对自己感兴趣,其唯一责任是通知它的各 观察者 缺点:一个观察者不知道其它观察者的存在, 出现意外的更新时,难以捕捉错误 Institute of Computer Software Nanjing University 2018/11/8
Implementation 创建目标到其观察者之间的映射 观察多个目标:扩展update接口 谁触发更新 由目标对象的状态设定操作自动调用notify 客户调用notify 对已删除目标的悬挂引用:一种避免悬挂引用的方法 是, 当一个目标被删除时,让它通知它的观察者将对 该目标的引用复位。一般来说,不能简单地删除观察 者 在发出通知前确保目标的状态自身是一致的 Institute of Computer Software Nanjing University 2018/11/8
Implementation 避免特定于观察者的更新协议—推/拉模型 显式地指定感兴趣的改变:比如扩展目标的注 册接口,让各观察者注册为仅对特定事件感兴 趣 封装复杂的更新语义,引入ChangeManager 结合目标类和观察者类:特定于语言,比如 Smalltalk Institute of Computer Software Nanjing University 2018/11/8
Related Patterns Mediator: 通过封装复杂的更新语义, ChangeManager充当目标和观察者之间的中介者 。 Singleton: ChangeManager可使用Singleton模式来 保证它是唯一的并且是可全局访问的。 Institute of Computer Software Nanjing University 2018/11/8
Java Support for Observer Pattern The java.util package provides an Observable class and an Observer interface: Institute of Computer Software Nanjing University 2018/11/8
Java AWT AWT 1.0: 责任链 AWT 1.1+: DEM (Delegation Event Model) addXXXListener removeXXXListener Institute of Computer Software Nanjing University 2018/11/8
State Aliases:objects for states Intent Motivation 允许一个对象在其内部状态改变时改变它的行为。 对象看起来似乎修改了它的类。 Motivation Institute of Computer Software Nanjing University 2018/11/8
Example Institute of Computer Software Nanjing University 2018/11/8
Applicability 一个对象的行为取决于它的状态, 并且它必须在 运行时刻根据状态改变它的行为。 一个操作中含有庞大的多分支的条件语句,且 这些分支依赖于该对象的状态。这个状态通常 用一个或多个枚举常量表示。通常, 有多个操作 包含这一相同的条件结构。State模式将每一个 条件分支放入一个独立的类中。这使得你可以 根据对象自身的情况将对象的状态作为一个对 象,这一对象可以不依赖于其他对象而独立变 化。 Institute of Computer Software Nanjing University 2018/11/8
Structure Sample Code Institute of Computer Software 2018/11/8 Nanjing University 2018/11/8
Participants Context State ConcreteState subclasses 定义客户感兴趣的接口 Institute of Computer Software Nanjing University 2018/11/8
Collaboration Context将与状态相关的请求委托给当前的 ConcreteState对象处理。 Context可将自身作为一个参数传递给处理该请求的状 态对象。这使得状态对象在必要时可访问Context Context是客户使用的主要接口。客户可用状态对象来 配置一个Context,一旦一个Context配置完毕, 它的客 户不再需要直接与状态对象打交道。 Context或ConcreteState子类都可决定哪个状态是另外 哪一个的后继者,以及是在何种条件下进行状态转换 。 Institute of Computer Software Nanjing University 2018/11/8
Consequences 它将与特定状态相关的行为局部化,并且将不 同状态的行为分割开来 它使得状态转换显式化 State对象可被共享 Institute of Computer Software Nanjing University 2018/11/8
Implementation 谁定义状态转换? Context or State子类 or 外部事 件 基于表的另一种方法 表驱动的方法着重于定义状态转换 创建和销毁State对象 仅当需要State对象时才创建并随后销毁 提前创建并且始终不销毁 使用动态继承:改变一个响应特定请求的行为 可以用在运行时刻改变这个对象的类的方法实 现 Institute of Computer Software Nanjing University 2018/11/8
Related Patterns Flyweight模式解释了何时以及怎样共享状态对 象 状态对象通常是Singleton Institute of Computer Software Nanjing University 2018/11/8
Strategy Aliases:Policy Intent Motivation 定义一系列的算法,把它们一个个封装起来, 并且使它们可相 互替换。本模式使得算法可独立于使用它的客户而变化。 Motivation 有些算法对于某些类是必不可少的,但是不适合于硬编进类 中。客户可能需要算法的多种不同实现,允许增加新的算法 实现或者改变现有的算法实现 我们可以把这样的算法封装到单独的类中,称为strategy Institute of Computer Software Nanjing University 2018/11/8
Example Institute of Computer Software Nanjing University 2018/11/8
Applicability 许多相关的类仅仅是行为有异。“策略”提供了一种用 多个行为中的一个行为来配置一个类的方法。 需要使用一个算法的不同变体。 算法使用客户不应该知道的数据。可使用策略模式以 避免暴露复杂的、与算法相关的数据结构。 一个类定义了多种行为, 并且这些行为在这个类的操 作中以多个条件语句的形式出现。将相关的条件分支 移入它们各自的Strategy类中以代替这些条件语句。 Institute of Computer Software Nanjing University 2018/11/8
Structure Sample Code Institute of Computer Software 2018/11/8 Nanjing University 2018/11/8
Participants Strategy ConcreteStrategy Context 定义所有支持的算法的公共接口。Context使用这个 接口来调用某ConcreteStrategy定义的算法。 ConcreteStrategy Strategy接口实现某具体算法。 Context 用一个ConcreteStrategy对象来配置。 维护一个对Strategy对象的引用。 可定义一个接口来让Strategy访问它的数据。 Institute of Computer Software Nanjing University 2018/11/8
Collaboration Strategy和Context相互作用以实现选定的算法。当算 法被调用时, Context可以将该算法所需要的所有数据 都传递给该Strategy 。或者, Context可以将自身作为 一个参数传递给Strategy操作。这就让Strategy在需要 时可以回调Context 。 Context将它的客户的请求转发给它的Strategy 。客户 通常创建并传递一个ContextStrategy对象给该Context ;这样, 客户仅与Context交互。通常有一系列的 ContextStrategy类可供客户从中选择。 Institute of Computer Software Nanjing University 2018/11/8
Consequences 相关算法系列:Strategy类层次定义了一系列可 重用的算法或行为。 一个替代继承的方法 消除了一些条件语句 实现的选择:时间/空间的权衡 客户必须了解不同的Strategy Strategy和Context之间的通信开销 增加了对象的数目 Institute of Computer Software Nanjing University 2018/11/8
Implementation 定义Strategy和Context接口:必须使得 ConcreteStrategy能够有效访问它所需要的 Context中的任何数据 Context将数据放在参数中传给Strategy Context将自身作为一个参数传给Strategy (C++)将Strategy作为模板参数 使Strategy对象成为可选的 Institute of Computer Software Nanjing University 2018/11/8
Related Patterns Flyweight:Strategy对象经常是很好的Flyweight 对象 Institute of Computer Software Nanjing University 2018/11/8
Template Method Intent Motivation Institute of Computer Software Nanjing University 2018/11/8
Example Template method 一个模板方法用一些抽象的操作定义一个算法,而子类将重定义这些操作以提供具体的行为。 Institute of Computer Software Nanjing University 2018/11/8
Applicability 一次性实现一个算法的不变的部分,并将可变 的行为留给子类来实现 各子类中公共的行为应被提取出来并集中到一 个公共父类中以避免代码重复--“重分解以一般 化” 控制子类扩展。模板方法只在特定点调用“hook” 操作,这样就只允许在这些点进行扩展 Institute of Computer Software Nanjing University 2018/11/8
Structure 类行为模式 Institute of Computer Software 2018/11/8 Nanjing University 2018/11/8
Participants AbstractClass ConcreteClass 定义抽象的原语操作(primitive operation),具体的子 类将重定义它们以实现一个算法的各步骤 实现一个模板方法,定义一个算法的骨架。该模板 方法不仅调用原语操作,也调用定义在 AbstractClass或其他对象中的操作。 ConcreteClass 实现原语操作以完成算法中与特定子类相关的步骤 Institute of Computer Software Nanjing University 2018/11/8
Collaboration ConcreteClass靠AbstractClass来实现算法中不变 的步骤 Sample Code Institute of Computer Software Nanjing University 2018/11/8
Consequences 模板方法是一种代码复用的基本技术。它们在 类库中尤为重要,它们提取了类库中的公共行 为。 模板方法导致一种反向的控制结构。 回顾下“好莱坞原则” 子类可以置换掉父类的可变部分,但是子类却不可以改变模板方法所代表的顶级逻辑! Institute of Computer Software Nanjing University 2018/11/8
Consequences 模板方法调用下列类型的操作: 具体的操作(ConcreteClass或对客户类的操作) 具体的AbstractClass的操作(即通常对子类有用的 操作) 原语操作(即抽象操作) Factory Method 钩子操作(hook operations),它提供了缺省的行 为,子类可以在必要时进行扩展。一个钩子操作在 缺省操作通常是一个空操作 Institute of Computer Software Nanjing University 2018/11/8
继承作为复用的工具 不知其一:不知道何为继承,功能复用全部通 过委派 知其一不知其二:把原来使用委派的地方改为 使用继承,滥用继承 知其二:CARP,使用委派代替继承 知其三:恰当地使用继承 Institute of Computer Software Nanjing University 2018/11/8
Implementation 使用C++访问控制:在C++中,一个模板方法 调用的原语操作可以被定义为保护成员。这保 证它们只被模板方法调用。必须重定义的原语 操作须定义为纯虚函数。模板方法自身不需被 重定义;因此可以将模板方法定义为一个非虚 成员函数。 尽量减少原语操作 命名约定,e.g. “Do-” 思考题:Java中对模板方法的访问控制 Institute of Computer Software Nanjing University 2018/11/8
Related Patterns Factory Method模式常被模板方法调用。 Strategy:模板方法使用继承来改变算法的一部 分。Strategy使用委托来改变整个算法。 Institute of Computer Software Nanjing University 2018/11/8
Visitor Intent Motivation 表示一个作用于某对象结构中的各元素的操作。它 使你可以在不改变各元素的类的前提下定义作用于 这些元素的新操作。 Motivation 为了把一个操作作用于一个对象结构中,一种做法 是把这个操作分散到每一个节点上,导致系统难以 理解、维护和修改。改进方案是把这样的操作包装 到一个独立的对象(visitor)中。然后在遍历过程中把 此对象传递给被访问的元素。 Institute of Computer Software Nanjing University 2018/11/8
不用visitor的compiler例子 Operation Class TypeCheck GenerateCode PrettyPrint VariableRefNode AssignmentNode ... Institute of Computer Software Nanjing University 2018/11/8 117
使用visitor的compiler例子 class Class operation VariableRefNode AssignmentNode TypeCheckVisitor VisitVariableRef VisitAssignment GenerateCodeVisitor PrettyPrintVisitor 两个类层次! Institute of Computer Software Nanjing University 2018/11/8 118
Applicability 一个对象结构包含许多对象类,我们想执行一 些依赖于具体类的操作 要对一个对象结构中的对象进行很多不同的并 且不相关的操作,又不想改变这些对象类 定义对象结构的类很少改变,但是经常要在此 结构上定义新的操作。改变对象结构类,需要 重定义所有visitor的接口 Institute of Computer Software Nanjing University 2018/11/8
Structure Institute of Computer Software Nanjing University 2018/11/8
Participants Visitor ConcreteVisitor Element ConcreteElement 为该对象结构中ConcreteElement的每一个类声明一个Visit操作。该操作的名字 和特征标识了发送Visit请求给该访问者的那个类。这使得访问者可以确定正被 访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它 。 ConcreteVisitor 实现每个由Visitor声明的操作。每个操作实现本算法的一部分,而该算法片断 乃是对应于结构中对象的类。 ConcreteVisitor为该算法提供了上下文并存储它 的局部状态。这一状态常常在遍历该结构的过程中累积结果。 Element 定义一个Accept操作,它以一个访问者为参数。 ConcreteElement 实现Accept操作,该操作以一个访问者为参数。 ObjectStructure 能枚举它的元素。 可以提供一个高层的接口以允许该访问者访问它的元素。 可以是一个复合或是一个集合 Institute of Computer Software Nanjing University 2018/11/8
Collaboration Client先创建一个ConcreteVisitor,然后遍历 ObjectStructure Sample Code Institute of Computer Software Nanjing University 2018/11/8
Consequences 访问者模式使得易于增加新的操作 访问者集中相关的操作而分离无关的操作 通过类层次进行访问,与Iterator的不同(思考 题,有何不同?) 累积状态,当访问者对象访问对象结构中的每 个元素时,可能会累积状态 缺点:增加新的ConcreteElement类很困难 缺点:破坏封装,常常迫使提供访问元素内部 状态的公共操作 Institute of Computer Software Nanjing University 2018/11/8
Implementation Double-dispatch 双分派 谁负责遍历对象结构? Accept是一个double-dispatch操作。它的含义决定于 两个类型: Visitor的类型和Element的类型。双分派 使得访问者可以对每一个类的元素请求不同的操作 。 谁负责遍历对象结构? 对象结构 or 访问者 or 独立的迭代器对象? Institute of Computer Software Nanjing University 2018/11/8
单分派 VS. 多分派 分派(dispatch):根据对象类型而对方法进行的 选择 静态分派:发生在编译时期,分派根据静态类型信 息发生,Java通过overloading方法支持静态分派 动态分派:发生在运行时期,OO利用动态分派实 现方法置换产生的多态性,Java通过overriding方法 支持动态分派 方法的接收者和方法的参数统称为方法的宗量( 《Java与模式》) Institute of Computer Software Nanjing University 2018/11/8
单分派 VS. 多分派 单分派:根据一个宗量的类型进行方法的选择 ,e.g. C++,Java,Smalltalk 多分派:根据多个宗量的类型进行方法的选择 ,e.g. CLOS C++,Java: 静态的多分派:方法接收者类型(声明的类型)和方 法所有参量类型,(overload) 动态的单分派:方法接收者类型(实际类型),( override) 变量被声明时的类型叫做变量的静态类型(Static Type) 又叫明显类型(Apparent Type)。 变量所引用的对象的真实类型又叫做变量的实际类型(Actual Type)。 an example… Institute of Computer Software Nanjing University 2018/11/8
动态双分派 方案1:instanceof 方案2:通过两次方法调用达到双重分派 another example… 方案2是Visitor模式的精华所在! Institute of Computer Software Nanjing University 2018/11/8
Related Patterns Composite:访问者可以用于对一个由Composite 模式定义的对象结构进行操作。 Interpreter:访问者可以用于解释。 Institute of Computer Software Nanjing University 2018/11/8
Summary: Behavioral Patterns Strategy、Iterator、Mediator、State、Command 用一个对象来封装某些经常变化的特性,比如算法(Strategy) 、交互协议(Mediator)、与状态相关的行为(State) 、遍历方法 (Iterator) Mediator、Observer Observer建立起subject和observer之间的松耦合连接 Mediator把约束限制集中起来,-〉中心控制 Command、Chain of Responsibility、Interpreter Command模式侧重于命令的总体管理 Chain of Responsibility侧重于命令被正确处理 Interpreter用于复合结构中操作的执行过程 Institute of Computer Software Nanjing University 2018/11/8
Summary: Pattern As Christopher Alexander says: It is possible to make buildings by stringing together patterns, in a rather loose way. A building made like this, is an assembly of patterns. It is not dense. It is not profound. But it is also possible to put patterns together in such a way that many patterns overlap in the same physical space: the building is very dense; it has many meanings captured in a small space; and through this density, it becomes profound. A Pattern Language [AIX+77, page xli] Institute of Computer Software Nanjing University 2018/11/8
Summary: Pattern “以一种松散的方式把一些模式串接在一起来建 造建筑是可能的。这样的建筑仅仅是一些模式 的堆砌,而不紧凑。这不够深刻。然而另有一 种组合模式的方式,许多模式重叠在同一个物 理空间里:这样的建筑非常紧凑,在一小块空 间里集成了许多内涵;由于这种紧凑,它变得 深刻。” A Pattern Language [AIX+77, 第41页] Institute of Computer Software Nanjing University 2018/11/8
Apply Design Pattern In theory practice is the same as theory. In practice it isn't. —— Adam Smith 纸上得来终觉浅,绝知此事要躬行。 ——冬夜读书示子聿 (陆游·宋) Institute of Computer Software Nanjing University 2018/11/8
作业 实现支持多个checkpoints的Memento 实现Command模式为何有时会用到Composite模 式?实现Command模式为何有时用到Memento模 式?Visitor模式和Iterator模式有何异同? Institute of Computer Software Nanjing University 2018/11/8
作业 以下不用提交: 学习javax.swing.undo库的使用 研究java awt的Delegation Event Model的原理 阅读GoF的书。理解各模式的意图和原理。(Hint: 利用书中提供的C++实例或者课堂提供的Java实例 帮助理解和记忆) Institute of Computer Software Nanjing University 2018/11/8