看透设计模式-行为型模式

 本文主要讨论行为型模式正则表达式

职责链模式:

 

职责链模式(Chain of Responsibility  Pattern):避免请求发送者与接收者耦合在一块儿,让多个对象都有可能接收请求,将这些对象链接成一条链,而且沿着这条链传递请求,直到有对象处理它为止。职责链模式是一种对象行为型模式。算法

      职责链模式结构的核心在于引入了一个抽象处理者、职责链模式的核心在于抽象处理者类的设计
--------------------- 数据库

● Handler(抽象处理者):它定义了一个处理请求的接口,通常设计为抽象类,因为不一样的具体处理者处理请求的方式不一样,所以在其中定义了抽象请求处理方法。由于每个处理者的下家仍是一个处理者,所以在抽象处理者中定义了一个抽象处理者类型的对象(如结构图中的successor),做为其对下家的引用。经过该引用,处理者能够连成一条链。编程

● ConcreteHandler(具体处理者):它是抽象处理者的子类,能够处理用户请求,在具体处理者类中实现了抽象处理者中定义的抽象请求处理方法,在处理请求以前须要进行判断,看是否有相应的处理权限,若是能够处理请求就处理它,不然将请求转发给后继者;在具体处理者中能够访问链中下一个对象,以便请求的转发。设计模式

      在职责链模式里,不少对象由每个对象对其下家的引用而链接起来造成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪个对象最终处理这个请求,这使得系统能够在不影响客户端的状况下动态地从新组织链和分配责任。
---------------------  浏览器

ConcreteHandlerA ConcreteHandlerB的继承能够看作一个,加上successor的聚合引用关系,就两个 关系,因此通常来讲, 职责链模式是很容易理解和使用的。安全

具体处理者是抽象处理者的子类,它具备两大做用:第一是处理请求,不一样的具体处理者以不一样的形式实现抽象请求处理方法handleRequest();第二是转发请求,若是该请求超出了当前处理者类的权限,能够将该请求转发给下家。—— 须要检查 权限是否足够。数据结构

须要注意的是,职责链模式并不建立职责链,职责链的建立工做必须由系统的其余部分来完成,通常是在使用该职责链的客户端中建立职责链。职责链模式下降了请求的发送端和接收端之间的耦合,使多个对象都有机会处理这个请求。框架

 

  (1) 纯的职责链模式编程语言

      一个纯的职责链模式要求一个具体处理者对象只能在两个行为中选择一个:要么承担所有责任,要么将责任推给下家,不容许出现某一个具体处理者对象在承担了一部分或所有责任后又将责任向下传递的状况。并且在纯的职责链模式中,要求一个请求必须被某一个处理者对象所接收,不能出现某个请求未被任何一个处理者对象处理的状况。在前面的采购单审批实例中应用的是纯的职责链模式。

 

       (2)不纯的职责链模式

      在一个不纯的职责链模式中容许某个请求被一个具体处理者部分处理后再向下传递,或者一个具体处理者处理完某请求后其后继处理者能够继续处理该请求,并且一个请求能够最终不被任何处理者对象所接收。Java AWT 1.0中的事件处理模型应用的是不纯的职责链模式,其基本原理以下:因为窗口组件(如按钮、文本框等)通常都位于容器组件中,所以当事件发生在某一个组件上时,先经过组件对象的handleEvent()方法将事件传递给相应的事件处理方法,该事件处理方法将处理此事件,而后决定是否将该事件向上一级容器组件传播;上级容器组件在接到事件以后能够继续处理此事件并决定是否继续向上级容器组件传播,如此反复,直到事件到达顶层容器组件为止;若是一直传到最顶层容器仍没有处理方法,则该事件不予处理。每一级组件在接收到事件时,均可以处理此事件,而不论此事件是否在上一级已获得处理,还存在事件未被处理的状况。显然,这就是不纯的职责链模式,早期的Java AWT事件模型(JDK 1.0及更早)中的这种事件处理机制又叫事件浮升(Event Bubbling)机制。从Java.1.1之后,JDK使用观察者模式代替职责链模式来处理事件。目前,在JavaScript中仍然可使用这种事件浮升机制来进行事件处理。
---------------------  

  职责链模式的主要优势以下:

       (1) 职责链模式使得一个对象无须知道是其余哪个对象处理其请求,对象仅需知道该请求会被处理便可,接收者和发送者都没有对方的明确信息,且链中的对象不须要知道链的结构,由客户端负责链的建立,下降了系统的耦合度。

       (2) 请求处理对象仅需维持一个指向其后继者的引用,而不须要维持它对全部的候选处理者的引用,可简化对象的相互链接。

       (3) 在给对象分派职责时,职责链能够给咱们更多的灵活性,能够经过在运行时对该链进行动态的增长或修改来增长或改变处理一个请求的职责。

       (4) 在系统中增长一个新的具体请求处理者时无须修改原有系统的代码,只须要在客户端从新建链便可,从这一点来看是符合“开闭原则”的。

      

       2.主要缺点

      职责链模式的主要缺点以下:

       (1) 因为一个请求没有明确的接收者,那么就不能保证它必定会被处理,该请求可能一直到链的末端都得不处处理;一个请求也可能因职责链没有被正确配置而得不处处理。

       (2) 对于比较长的职责链,请求的处理可能涉及到多个处理对象,系统性能将受到必定影响,并且在进行代码调试时不太方便。

       (3) 若是建链不当,可能会形成循环调用,将致使系统陷入死循环。
---------------------  

3.适用场景

      在如下状况下能够考虑使用职责链模式:

       (1) 有多个对象能够处理同一个请求,具体哪一个对象处理该请求待运行时刻再肯定,客户端只需将请求提交到链上,而无须关心请求的处理对象是谁以及它是如何处理的。

       (2) 在不明确指定接收者的状况下,向多个对象中的一个提交一个请求。

       (3) 可动态指定一组对象处理请求,客户端能够动态建立职责链来处理请求,还能够改变链中处理者之间的前后次序。 
---------------------  

示例: 拦截器、过滤器、请假、报销审批(须要按额度 层层判断的那种)

拓展性:能够灵活的增长一个Handler。 由客户端能够动态建立职责链来处理请求。

简化: 只有一个继承关系+ 聚合关系,显然,都是不可或缺,于是没法再继续简化了

讨论

主要解决了 多重判断 的问题。就像跟女人同样,咱们都喜欢纯的,可是实际遇到不纯的,也很多,实属正常,不可强求必定要“纯的”。

Handler 是否能够有多个handlerRequest方法,多个successor? 显然是不能够的, 不然 问题就复杂了。

 

命令模式:

 

命令模式(Command Pattern):将一个请求封装为一个对象,从而让咱们可用不一样的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操做。命令模式是一种对象行为型模式,其别名为动做(Action)模式或事务(Transaction)模式。

       命令模式的定义比较复杂,提到了不少术语,例如“用不一样的请求对客户进行参数化”、“对请求排队”,“记录请求日志”、“支持可撤销操做”等,在后面咱们将对这些术语进行一一讲解。

       命令模式的核心在于引入了命令类,经过命令类来下降发送者和接收者的耦合度,请求发送者只需指定一个命令对象,再经过命令对象来调用请求接收者的处理方法
---------------------  

● Command(抽象命令类):抽象命令类通常是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,经过这些方法能够调用请求接收者的相关操做。

● ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动做绑定其中。在实现execute()方法时,将调用接收者对象的相关操做(Action)。

● Invoker(调用者):调用者即请求发送者,它经过命令对象来执行请求。一个调用者并不须要在设计时肯定其接收者,所以它只与抽象命令类之间存在关联关系。在程序运行时能够将一个具体命令对象注入其中,再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关操做。

● Receiver(接收者):接收者执行与请求相关的操做,它具体实现对请求的业务处理。
---------------------  

命令模式能够将请求发送者和接收者彻底解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只须要知道如何发送请求,而没必要知道如何完成请求

命令模式的本质是对请求进行封装,一个请求对应于一个命令,将发出命令的责任和执行命令的责任分割开。每个命令都是一个操做:请求的一方发出请求要求执行一个操做;接收的一方收到请求,并执行相应的操做。命令模式容许请求的一方和接收的一方独立开来,使得请求的一方没必要知道接收请求的一方的接口,更没必要知道请求如何被接收、操做是否被执行、什么时候被执行,以及是怎么被执行的。

       命令模式的关键在于引入了抽象命令类,请求发送者针对抽象命令类编程,只有实现了抽象命令类的具体命令才与请求接收者相关联。在最简单的抽象命令类中只包含了一个抽象的execute()方法,每一个具体命令类将一个Receiver类型的对象做为一个实例变量进行存储,从而具体指定一个请求的接收者,不一样的具体命令类提供了execute()方法的不一样实现,并调用不一样接收者的请求处理方法。
---------------------  

命令模式是一种使用频率很是高的设计模式,它能够将请求发送者与接收者解耦,请求发送者经过命令对象来间接引用请求接收者,使得系统具备更好的灵活性和可扩展性。在基于GUI的软件开发,不管是在电脑桌面应用仍是在移动应用中,命令模式都获得了普遍的应用。

 

       1. 主要优势

       命令模式的主要优势以下:

       (1) 下降系统的耦合度。因为请求者与接收者之间不存在直接引用,所以请求者与接收者之间实现彻底解耦,相同的请求者能够对应不一样的接收者,一样,相同的接收者也能够供不一样的请求者使用,二者之间具备良好的独立性。

       (2) 新的命令能够很容易地加入到系统中。因为增长新的具体命令类不会影响到其余类,所以增长新的具体命令类很容易,无须修改原有系统源代码,甚至客户类代码,知足“开闭原则”的要求。

       (3) 能够比较容易地设计一个命令队列或宏命令(组合命令)。

       (4) 为请求的撤销(Undo)和恢复(Redo)操做提供了一种设计和实现方案。

 

       2. 主要缺点

       命令模式的主要缺点以下:

       使用命令模式可能会致使某些系统有过多的具体命令类。由于针对每个对请求接收者的调用操做都须要设计一个具体命令类,所以在某些系统中可能须要提供大量的具体命令类,这将影响命令模式的使用。

 

       3. 适用场景

      在如下状况下能够考虑使用命令模式:

       (1) 系统须要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。请求调用者无须知道接收者的存在,也无须知道接收者是谁,接收者也无须关心什么时候被调用。

       (2) 系统须要在不一样的时间指定请求、将请求排队和执行请求。一个命令对象和请求的初始调用者能够有不一样的生命期,换言之,最初的请求发出者可能已经不在了,而命令对象自己仍然是活动的,能够经过该命令对象去调用请求接收者,而无须关心请求调用者的存在性,能够经过请求日志文件等机制来具体实现。

       (3) 系统须要支持命令的撤销(Undo)操做和恢复(Redo)操做。

       (4) 系统须要将一组操做组合在一块儿造成宏命令。
---------------------  

上面的UML图其实画得不太正确, 它并不能帮助咱们理解命令模式的精髓。

咱们看到,总共有: 一个继承关系+ 聚合关系+关联关系。须要特别注意的是, Receiver 应该是一个具体的 ConcreteReceiver , 它和 ConcreteCommand 一 一对应,这样,咱们才会明白, 原来是这样 解耦的啊!! 所谓的解耦, 无非就是“多了一个中间层” ,也就是多了command 层。Invoker 实际须要的是Receiver 的工做,可是如今多了 command ,如今Invoker 根本不须要的是Receiver 了,command 能够随意的切换Receiver , 从而 解耦了! 

 

讨论:

一个请求发送者可否对应多个请求接收者?如何实现? ___ 一个请求发送者能够对应多个请求接受者,将单个Command的引用改成List,而后在执行时遍历调用。

—— 命令队列,“批处理”

Receiver 是否认义成一个接口,·从而让ConcreteCommand 依赖接口,从而能够灵活的替换 ConcreteReceiver 呢?  能够这样,可是不是必须的。

Command 随意的切换Receiver , 是否会破坏 开闭原则?是的, ConcreteCommand 定下来的那一刻,那么其对应的ConcreteReceiver 的应该已经肯定了的,因此不能随意切换,尽管如此, 可是 Invoker 这一层是不须要变更的! 若是新增 Command ,Invoker也是不须要变更的!

Invoker 和 Command 的关系是 1v1 仍是1 v N ? 彻底能够是1vN ( 命令队列 ),client 去使用ConcreteCommand 便可。

Invoker 只要一个实例吗?no,不一样 Invoker 拥有不一样Command 便可。

Invoker 能够有多个 方法吗?能够的,好比一个计算器,有加减乘除等操做,每一个操做要求可以取消,即有 compute,undo 方法,那么各个具体的计算操做分别实现二者便可。

简化: 一个继承关系+ 聚合关系+关联关系,没法再简化了

示例:Struts2 的ActionServlet,Invoker 只有一个,实际的Action为command ,是很是多的,ConcreteCommand类 调用Receiver(对应?) 完成具体工做, 按需增长。

拓展性:新的命令能够很容易地加入到系统中。

控制面板、等等

 

解释器模式:

 

解释器模式(Interpreter Pattern):定义一个语言的文法,而且创建一个解释器来解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码。解释器模式是一种类行为型模式。

       因为表达式可分为终结符表达式和非终结符表达式,所以解释器模式的结构与组合模式的结构有些相似,但在解释器模式中包含更多的组成元素
---------------------  

 

● AbstractExpression(抽象表达式):在抽象表达式中声明了抽象的解释操做,它是全部终结符表达式和非终结符表达式的公共父类。

       ● TerminalExpression(终结符表达式):终结符表达式是抽象表达式的子类,它实现了与文法中的终结符相关联的解释操做,在句子中的每个终结符都是该类的一个实例。一般在一个解释器模式中只有少数几个终结符表达式类,它们的实例能够经过非终结符表达式组成较为复杂的句子。

       ● NonterminalExpression(非终结符表达式):非终结符表达式也是抽象表达式的子类,它实现了文法中非终结符的解释操做,因为在非终结符表达式中能够包含终结符表达式,也能够继续包含非终结符表达式,所以其解释操做通常经过递归的方式来完成。

       ● Context(环境类):环境类又称为上下文类,它用于存储解释器以外的一些全局信息,一般它临时存储了须要解释的语句。

       当系统无须提供全局公共信息时能够省略环境类,可根据实际状况决定是否须要环境类。

---------------------  

TerminalExpression 不可再细分,NonterminalExpression 是TerminalExpression 和 其余NonterminalExpression  的组合。

总结:

解释器模式为自定义语言的设计和实现提供了一种解决方案,它用于定义一组文法规则并经过这组文法规则来解释语言中的句子。虽然解释器模式的使用频率不是特别高,可是它在正则表达式、XML文档解释等领域仍是获得了普遍使用。与解释器模式相似,目前还诞生了不少基于抽象语法树的源代码处理工具,例如Eclipse中的Eclipse AST,它能够用于表示Java语言的语法结构,用户能够经过扩展其功能,建立本身的文法规则。

      1. 主要优势

      解释器模式的主要优势以下:

      (1) 易于改变和扩展文法。因为在解释器模式中使用类来表示语言的文法规则,所以能够经过继承等机制来改变或扩展文法。

      (2) 每一条文法规则均可以表示为一个类,所以能够方便地实现一个简单的语言。

      (3) 实现文法较为容易。在抽象语法树中每个表达式节点类的实现方式都是类似的,这些类的代码编写都不会特别复杂,还能够经过一些工具自动生成节点类代码。

      (4) 增长新的解释表达式较为方便。若是用户须要增长新的解释表达式只须要对应增长一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合“开闭原则”。

      2. 主要缺点

      解释器模式的主要缺点以下:

      (1) 对于复杂文法难以维护。在解释器模式中,每一条规则至少须要定义一个类,所以若是一个语言包含太多文法规则,类的个数将会急剧增长,致使系统难以管理和维护,此时能够考虑使用语法分析程序等方式来取代解释器模式。

      (2) 执行效率较低。因为在解释器模式中使用了大量的循环和递归调用,所以在解释较为复杂的句子时其速度很慢,并且代码的调试过程也比较麻烦。

      3. 适用场景

      在如下状况下能够考虑使用解释器模式:

      (1) 能够将一个须要解释执行的语言中的句子表示为一个抽象语法树。

      (2) 一些重复出现的问题能够用一种简单的语言来进行表达。

      (3) 一个语言的文法较为简单。

      (4) 执行效率不是关键问题。【注:高效的解释器一般不是经过直接解释抽象语法树来实现的,而是须要将它们转换成其余形式,使用解释器模式的执行效率并不高。】
---------------------  

迭代器模式:

 

在软件开发中,咱们常常须要使用聚合对象来存储一系列数据。聚合对象拥有两个职责:一是存储数据;二是遍历数据。从依赖性来看,前者是聚合对象的基本职责;然后者既是可变化的,又是可分离的。所以,能够将遍历数据的行为从聚合对象中分离出来,封装在一个被称之为“迭代器”的对象中,由迭代器来提供遍历聚合对象内部数据的行为,这将简化聚合对象的设计,更符合“单一职责原则”的要求。

       迭代器模式定义以下:

迭代器模式(Iterator Pattern):提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标(Cursor)。迭代器模式是一种对象行为型模式。

       在迭代器模式结构中包含聚合和迭代器两个层次结构,考虑到系统的灵活性和可扩展性,在迭代器模式中应用了工厂方法模式
---------------------  

理解迭代器模式中具体聚合类与具体迭代器类之间存在的依赖关系和关联关系。 —— 相对于相关关联。

主要优势

       迭代器模式的主要优势以下:

       (1) 它支持以不一样的方式遍历一个聚合对象,在同一个聚合对象上能够定义多种遍历方式。在迭代器模式中只须要用一个不一样的迭代器来替换原有迭代器便可改变遍历算法,咱们也能够本身定义迭代器的子类以支持新的遍历方式。

       (2) 迭代器简化了聚合类。因为引入了迭代器,在原有的聚合对象中不须要再自行提供数据遍历等方法,这样能够简化聚合类的设计。

       (3) 在迭代器模式中,因为引入了抽象层,增长新的聚合类和迭代器类都很方便,无须修改原有代码,知足“开闭原则”的要求。

 

       2. 主要缺点

       迭代器模式的主要缺点以下:

       (1) 因为迭代器模式将存储数据和遍历数据的职责分离,增长新的聚合类须要对应增长新的迭代器类,类的个数成对增长,这在必定程度上增长了系统的复杂性。

       (2) 抽象迭代器的设计难度较大,须要充分考虑到系统未来的扩展,例如JDK内置迭代器Iterator就没法实现逆向遍历,若是须要实现逆向遍历,只能经过其子类ListIterator等来实现,而ListIterator迭代器没法用于操做Set类型的聚合对象。在自定义迭代器时,建立一个考虑全面的抽象迭代器并非件很容易的事情。

 

       3. 适用场景

       在如下状况下能够考虑使用迭代器模式:

       (1) 访问一个聚合对象的内容而无须暴露它的内部表示。将聚合对象的访问与内部数据的存储分离,使得访问聚合对象时无须了解其内部实现细节。

       (2) 须要为一个聚合对象提供多种遍历方式。

       (3) 为遍历不一样的聚合结构提供一个统一的接口,在该接口的实现类中为不一样的聚合结构提供不一样的遍历方式,而客户端能够一致性地操做该接口。
---------------------  

 

中介者模式:

从:

变成了:

若是在一个系统中对象之间存在多对多的相互关系,咱们能够将对象之间的一些交互行为从各个对象中分离出来,并集中封装在一个中介者对象中,并由该中介者进行统一协调,这样对象之间多对多的复杂关系就转化为相对简单的一对多关系。经过引入中介者来简化对象之间的复杂交互,中介者模式是“迪米特法则”的一个典型应用。
--------------------- 

中介者模式定义以下:

中介者模式(Mediator Pattern):用一个中介对象(中介者)来封装一系列的对象交互,中介者使各对象不须要显式地相互引用,从而使其耦合松散,并且能够独立地改变它们之间的交互。中介者模式又称为调停者模式,它是一种对象行为型模式。

      在中介者模式中,咱们引入了用于协调其余对象/类之间相互调用的中介者类,为了让系统具备更好的灵活性和可扩展性,一般还提供了抽象中介者
---------------------  

 中介者模式 感受跟 外观模式 很像,其实否则,中介者模式 针对的是 系统内部, 相互的调用,双向的。 外观模式 关注的是 外部系统对内部各个系统的调用,是单向的。

● Mediator(抽象中介者):它定义一个接口,该接口用于与各同事对象之间进行通讯。

      ● ConcreteMediator(具体中介者):它是抽象中介者的子类,经过协调各个同事对象来实现协做行为,它维持了对各个同事对象的引用。

      ● Colleague(抽象同事类):它定义各个同事类公有的方法,并声明了一些抽象方法来供子类实现,同时它维持了一个对抽象中介者类的引用,其子类能够经过该引用来与中介者通讯。

      ● ConcreteColleague(具体同事类):它是抽象同事类的子类;每个同事对象在须要和其余同事对象通讯时,先与中介者通讯,经过中介者来间接完成与其余同事类的通讯;在具体同事类中实现了在抽象同事类中声明的抽象方法。

 

      中介者模式的核心在于中介者类的引入,在中介者模式中,中介者类承担了两方面的职责:

       (1) 中转做用(结构性):经过中介者提供的中转做用,各个同事对象就再也不须要显式引用其余同事,当须要和其余同事进行通讯时,可经过中介者来实现间接调用。该中转做用属于中介者在结构上的支持。

      (2) 协调做用(行为性):中介者能够更进一步的对同事之间的关系进行封装,同事能够一致的和中介者进行交互,而不须要指明中介者须要具体怎么作,中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。该协调做用属于中介者在行为上的支持。
---------------------  

在具体同事类ConcreteColleague中实现了在抽象同事类中声明的方法,其中方法method1()是同事类的自身方法(Self-Method),用于处理本身的行为,而方法method2()是依赖方法(Depend-Method),用于调用在中介者中定义的方法,依赖中介者来完成相应的行为,例如调用另外一个同事类的相关方法
---------------------  

如何理解同事类中的自身方法与依赖方法? 一个有外部依赖Mediator ,一个没有。

 

中介者模式将一个网状的系统结构变成一个以中介者对象为中心的星形结构,在这个星型结构中,使用中介者对象与其余对象的一对多关系来取代原有对象之间的多对多关系。中介者模式在事件驱动类软件中应用较为普遍,特别是基于GUI(Graphical User Interface,图形用户界面)的应用软件,此外,在类与类之间存在错综复杂的关联关系的系统中,中介者模式都能获得较好的应用。

 

       1. 主要优势

       中介者模式的主要优势以下:

       (1) 中介者模式简化了对象之间的交互,它用中介者和同事的一对多交互代替了原来同事之间的多对多交互,一对多关系更容易理解、维护和扩展,将本来难以理解的网状结构转换成相对简单的星型结构。

      (2) 中介者模式可将各同事对象解耦。中介者有利于各同事之间的松耦合,咱们能够独立的改变和复用每个同事和中介者,增长新的中介者和新的同事类都比较方便,更好地符合“开闭原则”。

      (3) 能够减小子类生成,中介者将本来分布于多个对象间的行为集中在一块儿,改变这些行为只需生成新的中介者子类便可,这使各个同事类可被重用,无须对同事类进行扩展。

 

      2. 主要缺点

      中介者模式的主要缺点以下:

      在具体中介者类中包含了大量同事之间的交互细节,可能会致使具体中介者类很是复杂,使得系统难以维护。

 

      3. 适用场景

      在如下状况下能够考虑使用中介者模式:

      (1) 系统中对象之间存在复杂的引用关系,系统结构混乱且难以理解。

      (2) 一个对象因为引用了其余不少对象而且直接和这些对象通讯,致使难以复用该对象。

      (3) 想经过一个中间类来封装多个类中的行为,而又不想生成太多的子类。能够经过引入中介者类来实现,在中介者中定义对象交互的公共行为,若是须要改变行为则能够增长新的具体中介者类。
---------------------  

拓展性: 若是须要引入新的具体同事类,只须要继承抽象同事类并实现其中的方法便可,因为具体同事类之间并没有直接的引用关系,所以原有全部同事类无须进行任何修改,它们与新增同事对象之间的交互能够经过修改或者增长具体中介者类来实现;若是须要在原有系统中增长新的具体中介者类,只须要继承抽象中介者类(或已有的具体中介者类)并覆盖其中定义的方法便可,在新的具体中介者中能够经过不一样的方式来处理对象之间的交互,也能够增长对新增同事的引用和调用。在客户端中只须要修改少量代码(若是引入配置文件的话有时能够不修改任何代码)就能够实现中介者的更换。 

 

讨论:

ConcreteMediator是必须的吗? 感受若是简单的话,能够不用吧~!

Mediator 一般有哪些接口方法? 协调各个 Colleague 的各类方法, 会不会很是多?

 ConcreteColleague 若是没有统一实现Colleague 怎么办? 这种状况 可以经过中介来 协调吗?

 

我的感受这个设计模式, 不算是真正的设计模式,不如说成一个 设计思想 更好吧~!

 

备忘录模式:

备忘录模式提供了一种状态恢复的实现机制,使得用户能够方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可使用暂时存储起来的备忘录将状态复原,当前不少软件都提供了撤销(Undo)操做,其中就使用了备忘录模式。

      备忘录模式定义以下:

备忘录模式(Memento Pattern):在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象以外保存这个状态,这样能够在之后将对象恢复到原先保存的状态。它是一种对象行为型模式,其别名为Token。

      备忘录模式的核心是备忘录类以及用于管理备忘录的负责人类的设计
---------------------

● Originator(原发器):它是一个普通类,能够建立一个备忘录,并存储它的当前内部状态,也可使用备忘录来恢复其内部状态,通常将须要保存内部状态的类设计为原发器。

      ●Memento(备忘录):存储原发器的内部状态,根据原发器来决定保存哪些内部状态。备忘录的设计通常能够参考原发器的设计,根据实际须要肯定备忘录类中的属性。须要注意的是,除了原发器自己与负责人类以外,备忘录对象不能直接供其余类使用,原发器的设计在不一样的编程语言中实现机制会有所不一样。

      ●Caretaker(负责人):负责人又称为管理者,它负责保存备忘录,可是不能对备忘录的内容进行操做或检查。在负责人类中能够存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象,也无须知道对象的实现细节。

      理解备忘录模式并不难,但关键在于如何设计备忘录类和负责人类。因为在备忘录中存储的是原发器的中间状态,所以须要防止原发器之外的其余对象访问备忘录,特别是不容许其余对象来修改备忘录。
---------------------  

在设计备忘录类时须要考虑其封装性,除了Originator类,不容许其余类来调用备忘录类Memento的构造函数与相关方法,若是不考虑封装性,容许其余类调用setState()等方法,将致使在备忘录中保存的历史状态发生改变,经过撤销操做所恢复的状态就再也不是真实的历史状态,备忘录模式也就失去了自己的意义。
--------------------- 

对于负责人类Caretaker,它用于保存备忘录对象,并提供getMemento()方法用于向客户端返回一个备忘录对象,原发器经过使用这个备忘录对象能够回到某个历史状态

 

再谈备忘录的封装
      备忘录是一个很特殊的对象,只有原发器对它拥有控制的权力,负责人只负责管理,而其余类没法访问到备忘录,所以咱们须要对备忘录进行封装。

      为了实现对备忘录对象的封装,须要对备忘录的调用进行控制,对于原发器而言,它能够调用备忘录的全部信息,容许原发器访问返回到先前状态所需的全部数据;对于负责人而言,只负责备忘录的保存并将备忘录传递给其余对象;对于其余对象而言,只须要从负责人处取出备忘录对象并将原发器对象的状态恢复,而无须关心备忘录的保存细节。理想的状况是只容许生成该备忘录的那个原发器访问备忘录的内部状态。

      在实际开发中,原发器与备忘录之间的关系是很是特殊的,它们要分享信息而不让其余类知道,实现的方法因编程语言的不一样而有所差别,在C++中可使用friend关键字,让原发器类和备忘录类成为友元类,互相之间能够访问对象的一些私有的属性;在Java语言中能够将原发器类和备忘录类放在一个包中,让它们之间知足默认的包内可见性,也能够将备忘录类做为原发器类的内部类,使得只有原发器才能够访问备忘录中的数据,其余对象都没法使用备忘录中的数据。
---------------------  

备忘录模式在不少软件的使用过程当中广泛存在,可是在应用软件开发中,它的使用频率并不过高,由于如今不少基于窗体和浏览器的应用软件并无提供撤销操做。若是须要为软件提供撤销功能,备忘录模式无疑是一种很好的解决方案。在一些字处理软件、图像编辑软件、数据库管理系统等软件中备忘录模式都获得了很好的应用。

 

      1.主要优势

      备忘录模式的主要优势以下:

      (1)它提供了一种状态恢复的实现机制,使得用户能够方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可使用暂时存储起来的备忘录将状态复原。

      (2)备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其余代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象能够实现屡次撤销操做。

 

      2.主要缺点

      备忘录模式的主要缺点以下:

      资源消耗过大,若是须要保存的原发器类的成员变量太多,就不可避免须要占用大量的存储空间,每保存一次对象的状态都须要消耗必定的系统资源。

 

      3.适用场景

      在如下状况下能够考虑使用备忘录模式:

      (1)保存一个对象在某一个时刻的所有状态或部分状态,这样之后须要时它可以恢复到先前的状态,实现撤销操做。

      (2)防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。
---------------------  

 

 

讨论:

可否经过原型模式来建立备忘录对象?系统该如何设计? —— 也是能够吧。

 

观察者模式:

 

 

观察者模式是使用频率最高的设计模式之一,它用于创建一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其余对象,其余对象将相应做出反应。在观察者模式中,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标能够对应多个观察者,并且这些观察者之间能够没有任何相互联系,能够根据须要增长和删除观察者,使得系统更易于扩展。

      观察者模式定义以下:

观察者模式(Observer Pattern):定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆获得通知并被自动更新。观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式。

      观察者模式结构中一般包括观察目标和观察者两个继承层次结构,
---------------------  

● Subject(目标):目标又称为主题,它是指被观察的对象。在目标中定义了一个观察者集合,一个观察目标能够接受任意数量的观察者来观察,它提供一系列方法来增长和删除观察者对象,同时它定义了通知方法notify()。目标类能够是接口,也能够是抽象类或具体类。

      ● ConcreteSubject(具体目标):具体目标是目标类的子类,一般它包含有常常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;同时它还实现了在目标类中定义的抽象业务逻辑方法(若是有的话)。若是无须扩展目标类,则具体目标类能够省略。

      ● Observer(观察者):观察者将对观察目标的改变作出反应,观察者通常定义为接口,该接口声明了更新数据的方法update(),所以又称为抽象观察者。

      ● ConcreteObserver(具体观察者):在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态须要和具体目标的状态保持一致;它实现了在抽象观察者Observer中定义的update()方法。一般在实现时,能够调用具体目标类的attach()方法将本身添加到目标类的集合中或经过detach()方法将本身从目标类的集合中删除。

      观察者模式描述了如何创建对象与对象之间的依赖关系,以及如何构造知足这种需求的系统。观察者模式包含观察目标和观察者两类对象,一个目标能够有任意数目的与之相依赖的观察者,一旦观察目标的状态发生改变,全部的观察者都将获得通知。做为对这个通知的响应,每一个观察者都将监视观察目标的状态以使其状态与目标状态同步,这种交互也称为发布-订阅(Publish-Subscribe)。观察目标是通知的发布者,它发出通知时并不须要知道谁是它的观察者,能够有任意数目的观察者订阅它并接收通知。
---------------------  

观察者模式总结
      观察者模式是一种使用频率很是高的设计模式,不管是移动应用、Web应用或者桌面应用,观察者模式几乎无处不在,它为实现对象之间的联动提供了一套完整的解决方案,凡是涉及到一对一或者一对多的对象交互场景均可以使用观察者模式。观察者模式普遍应用于各类编程语言的GUI事件处理的实现,在基于事件的XML解析技术(如SAX2)以及Web事件处理中也都使用了观察者模式。

      1.主要优势

      观察者模式的主要优势以下:

      (1) 观察者模式能够实现表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,并抽象了更新接口,使得能够有各类各样不一样的表示层充当具体观察者角色

      (2) 观察者模式在观察目标和观察者之间创建一个抽象的耦合。观察目标只须要维持一个抽象观察者的集合,无须了解其具体观察者。因为观察目标和观察者没有紧密地耦合在一块儿,所以它们能够属于不一样的抽象化层次。

      (3) 观察者模式支持广播通讯,观察目标会向全部已注册的观察者对象发送通知,简化了一对多系统设计的难度。

      (4) 观察者模式知足“开闭原则”的要求,增长新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的状况下,增长新的观察目标也很方便。

      2.主要缺点

      观察者模式的主要缺点以下:

      (1) 若是一个观察目标对象有不少直接和间接观察者,将全部的观察者都通知到会花费不少时间。

      (2) 若是在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间进行循环调用,可能致使系统崩溃。

      (3) 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

      3.适用场景

      在如下状况下能够考虑使用观察者模式:

      (1) 一个抽象模型有两个方面,其中一个方面依赖于另外一个方面,将这两个方面封装在独立的对象中使它们能够各自独立地改变和复用。

      (2) 一个对象的改变将致使一个或多个其余对象也发生改变,而并不知道具体有多少对象将发生改变,也不知道这些对象是谁。

      (3) 须要在系统中建立一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可使用观察者模式建立一种链式触发机制。
---------------------  

 

状态模式:

 

状态模式用于解决系统中复杂对象的状态转换以及不一样状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间能够进行转换,并且对象在不一样状态下行为不相同时可使用状态模式。状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态能够灵活变化,对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,不管对于何种状态的对象,客户端均可以一致处理。

       状态模式定义以下:

状态模式(State Pattern):容许一个对象在其内部状态改变时改变它的行为,对象看起来彷佛修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。

       在状态模式中引入了抽象状态类和具体状态类,它们是状态模式的核心
---------------------  

● Context(环境类):环境类又称为上下文类,它是拥有多种状态的对象。因为环境类的状态存在多样性且在不一样状态下对象的行为有所不一样,所以将状态独立出去造成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。

       ● State(抽象状态类):它用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各类不一样状态对应的方法,而在其子类中实现类这些方法,因为不一样状态下对象的行为可能不一样,所以在不一样子类中方法的实现可能存在不一样,相同的方法能够写在抽象状态类中。

       ● ConcreteState(具体状态类):它是抽象状态类的子类,每个子类实现一个与环境类的一个状态相关的行为,每个具体状态类对应环境的一个具体状态,不一样的具体状态类其行为有所不一样。

       在状态模式中,咱们将对象在不一样状态下的行为封装到不一样的状态类中,为了让系统具备更好的灵活性和可扩展性,同时对各状态下的共有行为进行封装,咱们须要对状态进行抽象,引入了抽象状态类角色
---------------------  

在状态模式的使用过程当中,一个对象的状态之间还能够进行相互转换,一般有两种实现状态转换的方式:

       (1) 统一由环境类来负责状态之间的转换  

       (2) 由具体状态类来负责状态之间的转换

 

状态模式 的状态是 根据外部提供 数据, 动态进行变化的, 某条件下是这个状态,其余条件是 另外的状态。 整体而言, 状态有 Context 驱动。。

 

状态模式总结
       状态模式将一个对象在不一样状态下的不一样行为封装在一个个状态类中,经过设置不一样的状态对象可让环境对象拥有不一样的行为,而状态转换的细节对于客户端而言是透明的,方便了客户端的使用。在实际开发中,状态模式具备较高的使用频率,在工做流和游戏开发中状态模式都获得了普遍的应用,例如公文状态的转换、游戏中角色的升级等。

 

       1. 主要优势

       状态模式的主要优势以下:

       (1) 封装了状态的转换规则,在状态模式中能够将状态的转换代码封装在环境类或者具体状态类中,能够对状态转换代码进行集中管理,而不是分散在一个个业务方法中。

       (2) 将全部与某个状态有关的行为放到一个类中,只须要注入一个不一样的状态对象便可使环境对象拥有不一样的行为。

       (3) 容许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块,状态模式可让咱们避免使用庞大的条件语句来将业务方法和状态转换代码交织在一块儿。

       (4) 可让多个环境对象共享一个状态对象,从而减小系统中对象的个数。

 

       2. 主要缺点

       状态模式的主要缺点以下:

       (1) 状态模式的使用必然会增长系统中类和对象的个数,致使系统运行开销增大。

       (2) 状态模式的结构与实现都较为复杂,若是使用不当将致使程序结构和代码的混乱,增长系统设计的难度。

       (3) 状态模式对“开闭原则”的支持并不太好,增长新的状态类须要修改那些负责状态转换的源代码,不然没法转换到新增状态;并且修改某个状态类的行为也需修改对应类的源代码。

 

      3. 适用场景

      在如下状况下能够考虑使用状态模式:

      (1) 对象的行为依赖于它的状态(如某些属性值),状态的改变将致使行为的变化。

      (2) 在代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会致使代码的可维护性和灵活性变差,不能方便地增长和删除状态,而且致使客户类与类库之间的耦合加强。
--------------------- 

适用于那些 状态不少,很复杂,行为随状态发生“大变化” 的状况。

“容许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块” ———— 然而状态模式 并不能彻底的消除 if else 条件语句块, 不过是把“巨大的条件语句块” 换成了 “比较小的条件语句块”

策略模式:

 

在策略模式中,咱们能够定义一些独立的类来封装不一样的算法,每个类封装一种具体的算法,在这里,每个封装算法的类咱们均可以称之为一种策略(Strategy),为了保证这些策略在使用时具备一致性,通常会提供一个抽象的策略类来作规则的定义,而每种算法则对应于一个具体策略类。

      策略模式的主要目的是将算法的定义与使用分开,也就是将算法的行为和环境分开,将算法的定义放在专门的策略类中,每个策略类封装了一种实现算法,使用算法的环境类针对抽象策略类进行编程,符合“依赖倒转原则”。在出现新的算法时,只须要增长一个新的实现了抽象策略类的具体策略类便可。策略模式定义以下:

策略模式(Strategy Pattern):定义一系列算法类,将每个算法封装起来,并让它们能够相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。策略模式是一种对象行为型模式。

      策略模式结构并不复杂,但咱们须要理解其中环境类Context的做用
---------------------  

 

● Context(环境类):环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时能够采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。

      ● Strategy(抽象策略类):它为所支持的算法声明了抽象方法,是全部策略类的父类,它能够是抽象类或具体类,也能够是接口。环境类经过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。

      ● ConcreteStrategy(具体策略类):它实现了在抽象策略类中声明的算法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。
--------------------- 

一个环境类Context可否对应多个不一样的策略等级结构?如何设计? 固然能够的, 多一个不一样等级结构 类型的Strategy 引用便可!

这个策略模式和简单工厂模式具备殊途同归之妙呀~ 他们都是将if...else这样难扩展和维护的代码经过继承的方式来解决,而后针对抽象类编程,使之扩展的时候知足“开闭原则

策略模式总结
      策略模式用于算法的自由切换和扩展,它是应用较为普遍的设计模式之一。策略模式对应于解决某一问题的一个算法族,容许用户从该算法族中任选一个算法来解决某一问题,同时能够方便地更换算法或者增长新的算法。只要涉及到算法的封装、复用和切换均可以考虑使用策略模式。

      1. 主要优势

      策略模式的主要优势以下:

      (1) 策略模式提供了对“开闭原则”的完美支持,用户能够在不修改原有系统的基础上选择算法或行为,也能够灵活地增长新的算法或行为。

      (2) 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当使用继承能够把公共的代码移到抽象策略类中,从而避免重复的代码。

      (3) 策略模式提供了一种能够替换继承关系的办法。若是不使用策略模式,那么使用算法的环境类就可能会有一些子类,每个子类提供一种不一样的算法。可是,这样一来算法的使用就和算法自己混在一块儿,不符合“单一职责原则”,决定使用哪种算法的逻辑和该算法自己混合在一块儿,从而不可能再独立演化;并且使用继承没法实现算法或行为在程序运行时的动态切换。

      (4) 使用策略模式能够避免多重条件选择语句。多重条件选择语句不易维护,它把采起哪种算法或行为的逻辑与算法或行为自己的实现逻辑混合在一块儿,将它们所有硬编码(Hard Coding)在一个庞大的多重条件选择语句中,比直接继承环境类的办法还要原始和落后。

      (5) 策略模式提供了一种算法的复用机制,因为将算法单独提取出来封装在策略类中,所以不一样的环境类能够方便地复用这些策略类。

      2. 主要缺点

      策略模式的主要缺点以下:

      (1) 客户端必须知道全部的策略类,并自行决定使用哪个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策略模式只适用于客户端知道全部的算法或行为的状况。

      (2) 策略模式将形成系统产生不少具体策略类,任何细小的变化都将致使系统要增长一个新的具体策略类。

      (3) 没法同时在客户端使用多个策略类,也就是说,在使用策略模式时,客户端每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另外一个策略类来完成剩余功能的状况。

      3. 适用场景

      在如下状况下能够考虑使用策略模式:

      (1) 一个系统须要动态地在几种算法中选择一种,那么能够将这些算法封装到一个个的具体算法类中,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,根据“里氏代换原则”和面向对象的多态性,客户端能够选择使用任何一个具体算法类,并只须要维持一个数据类型是抽象算法类的对象。

      (2) 一个对象有不少的行为,若是不用恰当的模式,这些行为就只好使用多重条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就能够避免使用难以维护的多重条件选择语句。

      (3) 不但愿客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法与相关的数据结构,能够提升算法的保密性与安全性。
--------------------- 

 

模板方法模式:

 

模板方法模式定义以下:

模板方法模式:定义一个操做中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类能够不改变一个算法的结构便可重定义该算法的某些特定步骤。

       模板方法模式是一种基于继承的代码复用技术,它是一种类行为型模式。

       模板方法模式是结构最简单的行为型设计模式,在其结构中只存在父类与子类之间的继承关系。经过使用模板方法模式,能够将一些复杂流程的实现步骤封装在一系列基本方法中,在抽象父类中提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而经过其子类来覆盖某些步骤,从而使得相同的算法框架能够有不一样的执行结果。模板方法模式提供了一个模板方法来定义算法框架,而某些具体步骤的实现能够在其子类中完成。
---------------------  

 模板方法模式结构比较简单,其核心是抽象类和其中的模板方法的设计

(1) AbstractClass(抽象类):在抽象类中定义了一系列基本操做(PrimitiveOperations),这些基本操做能够是具体的,也能够是抽象的,每个基本操做对应算法的一个步骤,在其子类中能够重定义或实现这些步骤。同时,在抽象类中实现了一个模板方法(Template Method),用于定义一个算法的框架,模板方法不只能够调用在抽象类中实现的基本方法,也能够调用在抽象类的子类中实现的基本方法,还能够调用其余对象中的方法。

       (2) ConcreteClass(具体子类):它是抽象类的子类,用于实如今父类中声明的抽象基本操做以完成子类特定算法的步骤,也能够覆盖在父类中已经实现的具体基本操做。
--------------------- 

AbstractClass的方法 分为

1 模板方法( 子类是不能动的!),

一个模板方法是定义在抽象类中的、把基本操做方法组合在一块儿造成一个总算法或一个总行为的方法。这个模板方法定义在抽象类中,并由子类不加以修改地彻底继承下来。模板方法是一个具体方法,它给出了一个顶层逻辑框架,而逻辑的组成步骤在抽象类中能够是具体方法,也能够是抽象方法。因为模板方法是具体方法,所以模板方法模式中的抽象层只能是抽象类,而不是接口。
---------------------  

2  基本方法。基本方法是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又能够分为三种:抽象方法(Abstract Method)、具体方法(Concrete Method)和钩子方法(Hook Method)。

 

模板方法模式效果与适用场景
       模板方法模式是基于继承的代码复用技术,它体现了面向对象的诸多重要思想,是一种使用较为频繁的模式。模板方法模式普遍应用于框架设计中,以确保经过父类来控制处理流程的逻辑顺序(如框架的初始化,测试流程的设置等)。

 

       5.1 模式优势
       模板方法模式的主要优势以下:

       (1) 在父类中形式化地定义一个算法,而由它的子类来实现细节的处理,在子类实现详细的处理算法时并不会改变算法中步骤的执行次序。

       (2) 模板方法模式是一种代码复用技术,它在类库设计中尤其重要,它提取了类库中的公共行为,将公共行为放在父类中,而经过其子类来实现不一样的行为,它鼓励咱们恰当使用继承来实现代码复用。

       (3) 可实现一种反向控制结构,经过子类覆盖父类的钩子方法来决定某一特定步骤是否须要执行。

       (4) 在模板方法模式中能够经过子类来覆盖父类的基本方法,不一样的子类能够提供基本方法的不一样实现,更换和增长新的子类很方便,符合单一职责原则和开闭原则。

 

       5.2 模式缺点
       模板方法模式的主要缺点以下:

       须要为每个基本方法的不一样实现提供一个子类,若是父类中可变的基本方法太多,将会致使类的个数增长,系统更加庞大,设计也更加抽象,此时,可结合桥接模式来进行设计。

 

       5.3 模式适用场景
       在如下状况下能够考虑使用模板方法模式:

       (1) 对一些复杂的算法进行分割,将其算法中固定不变的部分设计为模板方法和父类具体方法,而一些能够改变的细节由其子类来实现。即:一次性实现一个算法的不变部分,并将可变的行为留给子类来实现。

       (2) 各子类中公共的行为应被提取出来并集中到一个公共父类中以免代码重复。

       (3) 须要经过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。
---------------------  

 

访问者模式:

 

访问者模式是一种较为复杂的行为型设计模式,它包含访问者和被访问元素两个主要组成部分,这些被访问的元素一般具备不一样的类型,且不一样的访问者能够对它们进行不一样的访问操做。例如处方单中的各类药品信息就是被访问的元素,而划价人员和药房工做人员就是访问者。访问者模式使得用户能够在不修改现有系统的状况下扩展系统的功能,为这些不一样类型的元素增长新的操做。

      在使用访问者模式时,被访问元素一般不是单独存在的,它们存储在一个集合中,这个集合被称为“对象结构”,访问者经过遍历对象结构实现对其中存储的元素的逐个操做。

      访问者模式定义以下:

访问者模式(Visitor Pattern):提供一个做用于某对象结构中的各元素的操做表示,它使咱们能够在不改变各元素的类的前提下定义做用于这些元素的新操做。访问者模式是一种对象行为型模式。

      访问者模式的结构较为复杂
---------------------  

 

●Vistor(抽象访问者):抽象访问者为对象结构中每个具体元素类ConcreteElement声明一个访问操做,从这个操做的名称或参数类型能够清楚知道须要访问的具体元素的类型,具体访问者须要实现这些操做方法,定义对这些元素的访问操做。

      ●ConcreteVisitor(具体访问者):具体访问者实现了每一个由抽象访问者声明的操做,每个操做用于访问对象结构中一种类型的元素。

      ●Element(抽象元素):抽象元素通常是抽象类或者接口,它定义一个accept()方法,该方法一般以一个抽象访问者做为参数。【稍后将介绍为何要这样设计。】

      ●ConcreteElement(具体元素):具体元素实现了accept()方法,在accept()方法中调用访问者的访问方法以便完成对一个元素的操做。

      ● ObjectStructure(对象结构):对象结构是一个元素的集合,它用于存放元素对象,而且提供了遍历其内部元素的方法。它能够结合组合模式来实现,也能够是一个简单的集合对象,如一个List对象或一个Set对象。

      访问者模式中对象结构存储了不一样类型的元素对象,以供不一样访问者访问。访问者模式包括两个层次结构,一个是访问者层次结构,提供了抽象访问者和具体访问者,一个是元素层次结构,提供了抽象元素和具体元素。相同的访问者能够以不一样的方式访问不一样的元素,相同的元素能够接受不一样访问者以不一样访问方式访问。在访问者模式中,增长新的访问者无须修改原有系统,系统具备较好的可扩展性。

      在访问者模式中,抽象访问者定义了访问元素对象的方法,一般为每一种类型的元素对象都提供一个访问方法,而具体访问者能够实现这些访问方法。这些访问方法的命名通常有两种方式:一种是直接在方法名中标明待访问元素对象的具体类型,如visitElementA(ElementA elementA),还有一种是统一取名为visit(),经过参数类型的不一样来定义一系列重载的visit()方法。固然,若是全部的访问者对某一类型的元素的访问操做都相同,则能够将操做代码移到抽象访问者类中
---------------------  

在具体元素类ConcreteElementA的accept()方法中,经过调用Visitor类的visit()方法实现对元素的访问,并以当前对象做为visit()方法的参数。其具体执行过程以下:

      (1) 调用具体元素类的accept(Visitor visitor)方法,并将Visitor子类对象做为其参数;

      (2) 在具体元素类accept(Visitor visitor)方法内部调用传入的Visitor对象的visit()方法,如visit(ConcreteElementA elementA),将当前具体元素类对象(this)做为参数,如visitor.visit(this);

      (3) 执行Visitor对象的visit()方法,在其中还能够调用具体元素对象的业务方法。

      这种调用机制也称为“双重分派”,正由于使用了双重分派机制,使得增长新的访问者无须修改现有类库代码,只需将新的访问者对象做为参数传入具体元素对象的accept()方法,程序运行时将回调在新增Visitor类中定义的visit()方法,从而增长新的元素访问方式
---------------------  

访问者模式是否符合“开闭原则”?【从增长新的访问者和增长新的元素两方面考虑。】 新的访问者, 是的; 新的元素, 不是的!

 

访问者模式与组合模式联用

      在访问者模式中,包含一个用于存储元素对象集合的对象结构,咱们一般可使用迭代器来遍历对象结构,同时具体元素之间能够存在总体与部分关系,有些元素做为容器对象,有些元素做为成员对象,可使用组合模式来组织元素。引入组合模式后的访问者模式结构图如图

 

访问者模式总结
      因为访问者模式的使用条件较为苛刻,自己结构也较为复杂,所以在实际应用中使用频率不是特别高。当系统中存在一个较为复杂的对象结构,且不一样访问者对其所采起的操做也不相同时,能够考虑使用访问者模式进行设计。在XML文档解析、编译器的设计、复杂集合对象的处理等领域访问者模式获得了必定的应用。

1.主要优势

      访问者模式的主要优势以下:

(1) 增长新的访问操做很方便。使用访问者模式,增长新的访问操做就意味着增长一个新的具体访问者类,实现简单,无须修改源代码,符合“开闭原则”。

(2) 将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中。类的职责更加清晰,有利于对象结构中元素对象的复用,相同的对象结构能够供多个不一样的访问者访问。

(3) 让用户可以在不修改现有元素类层次结构的状况下,定义做用于该层次结构的操做。

2.主要缺点

      访问者模式的主要缺点以下:

(1) 增长新的元素类很困难。在访问者模式中,每增长一个新的元素类都意味着要在抽象访问者角色中增长一个新的抽象操做,并在每个具体访问者类中增长相应的具体操做,这违背了“开闭原则”的要求。

(2) 破坏封装。访问者模式要求访问者对象访问并调用每个元素对象的操做,这意味着元素对象有时候必须暴露一些本身的内部操做和内部状态,不然没法供访问者访问。

3.适用场景

      在如下状况下能够考虑使用访问者模式:

(1) 一个对象结构包含多个类型的对象,但愿对这些对象实施一些依赖其具体类型的操做。在访问者中针对每一种具体的类型都提供了一个访问操做,不一样类型的对象能够有不一样的访问操做。

(2) 须要对一个对象结构中的对象进行不少不一样的而且不相关的操做,而须要避免让这些操做“污染”这些对象的类,也不但愿在增长新操做时修改这些类。访问者模式使得咱们能够将相关的访问操做集中起来定义在访问者类中,对象结构能够被多个不一样的访问者类所使用,将对象自己与对象的访问操做分离。

(3) 对象结构中对象对应的类不多改变,但常常须要在此对象结构上定义新的操做。
--------------------- 

不得不说的是 ObjectStructure, 这个有点奇怪,对象结构? 咋理解?咱们看到ObjectStructure依赖了Element,Client是直接和ObjectStructure 交互的, 也就是Client经过ObjectStructure 选择了 具体的Element。 

 

========================================== THE END ==============================================

本文大部分摘抄于:https://blog.csdn.net/lovelion  

相关文章
相关标签/搜索