行为性设计模式:对象之间的通信。

职责链:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。

命令模式:用简单的对象表示软件命令的执行,支持登录和取消操作。

解释器模式:提供一个如何把语言元素包括在程序中的定义。

迭代器模式:提供一种顺序访问一个类中一系列数据的方式。

中介者模式:定义了如何用一个对象简化对象之间的通信,使对象之间不必相互了解。

备忘录模式:定义了如何保存一个类的实例内容,以便以后恢复它。

观察者模式:定义了一种把改动通知给多个对象的方式。

状态模式:允许一个对象在其内部状态改变时修改它的行为。

策略模式:将算法封装到类中。

模板方法模式:提供算法的一个抽象定义。

访问者模式:在不改变类的前提下,为一个类添加多种操作。

 

职责链: 允许多个类处理同一个请求,而不必了解彼此的功能。他在类之间提供一个松散的耦合。类之间唯一的联系就是相互之间的传递请求。请求在类之间传递,直到其中一个类处理它为止。当一个对象向多个对象发送相同的信息时,就需要一种策略来确定由哪个对象对所发送的信息进行处理,而这样的处理对象也只能有一个。使用case语句或if语句的方法会给程序的维护带来很大难度,这就需要职责链模式来完成。职责链模式将发送对象和接收对象进行了解耦,以更好的应对变化。职责链模式将接收对象形成一个链,发送对象将信息发送给接收对象链中的一个对象,这时,信息就沿着对象链向下传送,直到有一个对象对信息进行处理。

意图:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.
将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止.

角色及职责:

  • Handler(如Employee):定义一个处理请求的接口;
  • ConcreteHandler(如Server和Chef)处理它负责的请求;可访问它的后继者(Successor);如果可处理该请求,处理之;否则转发给后继者;(必须实现的接口)
  • Client:向职责链提交请求。

实现:

  • 层次结构下的职责链(类似于合成模式)

职责链由其Parent属性而形成,使其信息一直向上发送,直到被处理。

  • 非层次结构下的职责链

    利用其构造函数形成的职责链。消息发送时,如果接收对象不能处理就直接发送给其obj属性进行处理即可,如果其存在的话。

优点:松散耦合

缺点:效率低下,扩展性差

适用范围

  • "一个请求可能有多个接收者,但是最后真正的接受者只有一个",只有这时候请求发送者与接受者的偶合才有可能出现"变化脆弱"的症状,职责链的母的旧式将二者解耦,从而更好的应对变化

附注:如果请求传递到职责链的末尾依然得不到处理,应该有一个合理的缺省机制,这也是每一个接受对象的责任,而不是发出请求的对象的责任。应用了chain of responsibility模式后,对象的职责分派将更具灵活性。我们可以在运行时动态添加修改请求的处理职责。

 

命令模式:把申请特定操作的请求封装到一个对象中,并给对象一个众所周知的接口。命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。

意图:把发出命令的责任和执行命令的责任分割开,委派给不同的对象。

角色及职责:

  • 客户(Client)角色:创建了一个具体命令(ConcreteCommand)对象并确定其接收者
  • 命令(Command)角色:声明了一个给所有具体命令类的抽象接口。这是一个抽象角色
  • 具体命令(ConcreteCommand)角色:定义一个接受者和行为之间的弱耦合;实现Execute()方法,负责调用接收考的相应操作。Execute()方法通常叫做执方法
  • 请求者(Invoker)角色:负责调用命令对象执行请求,相关的方法叫做行动方法
  • 接收者(Receiver)角色:负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法

实现:轻:命令只是提供了一个请求者和接收者之间的耦合而己,命令代表请求者实现请求。重:命令实现所有的细节,包括请求所代表的操作,而不再需要接收者了。命令类动态地决定调用哪一个接收者类。其次是否支持undoredo。若一个命令类提供一个方法,比如叫unExecute(),以恢复其操作的效果,那么命令类就可以支持undoredo。具体命令类需要存储状态信息,包括:接收者对象实际上实施请求所代表的操作、 对接收者对象所作的操作所需要的参数、接收者类的最初的状态。接收者必须提供适当的方法,使命令类可以通过调用这个方法,以便接收者类恢复原有状态。如果只需要提供一层的undoredo,那么系统只需要存储最后被执行的那个命令对象。如果需要支持多层的undoredo,那么系统就需要存储曾经被执行过的命令的清单,清单能允许的最大的长度便是系统所支持的undoredo的层数。沿着清单逆着执行清单上的命令的反命令(unExecute())便是undo;沿着清单顺着执行清单上的命令便是redo

适用范围:

  • 使用命令模式作为"CallBack"在面向对象系统中的替代。"CallBack"讲的便是先将一个函数登记上,然后在以后调用此函数。
  • 需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去
  • 系统需要支持命令的撤消(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供redo()方法,以供客户端在需要时,再重新实施命令效果
  • 如果一个系统要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志里读回所有的数据更新命令,重新调用Execute()方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新
  • 一个系统需要支持交易(Transaction)。一个交易结构封装了一组数据更新命令。使用命令模式来实现交易结构可以使系统增加新的交易类型

优点:

  • 命令模式使新的命令很容易地被加入到系统里
  • 允许接收请求的一方决定是否要否决(Veto)请求
  • 能较容易地设计-个命令队列
  • 可以容易地实现对请求的UndoRedo
  • 在需要的情况下,可以较容易地将命令记入日志
  • 命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开
  • 命令类与其他任何别的类一样,可以修改和推广
  • 你可以把命令对象聚合在一起,合成为合成命令。比如宏命令便是合成命令的例子。合成命令是合成模式的应用
  • 由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易

缺点:

  • 使用命令模式会导致某些系统有过多的具体命令类。某些系统可能需要几十个,几百个甚至几千个具体命令类,这会使命令模式在这样的系统里变得不实际

附注:可以使用Map代替命令类。在invoke中可以轻松实现跟踪与日志。

 

解释器模式定义语言的文法,并且建立一个解释器来解释该语言中的句子。它属于类的行为模式。这里的语言意思是使用规定格式和语法的代码。如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。而且当文法简单、效率不是关键问题的时候效果最好。

意图:3

角色及职责:

  • 抽象表达式角色:声明一个抽象的解释操作,这个接口为所有具体表达式角色(抽象语法树中的节点)都要实现的。抽象语法树呢:《java与模式》中给的解释为:抽象语法树的每一个节点都代表一个语句,而在每个节点上都可以执行解释方法。这个解释方法的执行就代表这个语句被解释。由于每一个语句都代表这个语句被解释。由于每一个语句都代表一个常见的问题的实例,因此每一个节点上的解释操作都代表对一个问题实例的解答。
  • 终结符表达式角色:具体表达式。

    a) 实现与文法中的终结符相关联的解释操作

    b) 而且句子中的每个终结符需要该类的一个实例与之对应

  • 非终结符表达式角色:具体表达式。

    a) 文法中的每条规则R::=R1R2Rn都需要一个非终结符表带式角色

    b) 对于从R1Rn的每个符号都维护一个抽象表达式角色的实例变量

    c) 实现解释操作,解释一般要递归地调用表示从R1Rn的那些对象的解释操作

  • 上下文(环境)角色:包含解释器之外的一些全局信息。
  • 客户角色:

    a) 构建(或者被给定)表示该文法定义的语言中的一个特定的句子的抽象语法树

    b) 调用解释操作

优点:解释器模式提供了一个简单的方式来执行语法,而且容易修改或者扩展语法。一般系统中很多类使用相似的语法,可以使用一个解释器来代替为每一个规则实现一个解释器。而且在解释器中不同的规则是由不同的类来实现的,这样使得添加一个新的语法规则变得简单。

缺点:解释器模式对于复杂文法难以维护。每一个规则要对应一个处理类,而且类递归调用抽象表达式角色,关系混乱。

适用范围:

  • 当用户需要一个命令解释器分析用户命令时
  • 当程序需要分析一个代数串时
  • 当程序需要生成各种形式的输出

附注:解释器模式并没有说明如何创建一个抽象语法树,因此它的实现可以多种多样,在上面我们是直接在Test中提供的,当然还有更好、更专业的实现方式。对于终结符,GOF建议采用享元模式来共享它们的拷贝,因为它们要多次重复出现。但是考虑到享元模式的使用局限性,故系统中终结符重复的足够多的时候再考虑享元模式。

 

迭代器模式:提供一种方式可以连续的访问几个对象的所有元素而无须关注内在的描述方式。

角色及职责:

  • 迭代器(Iterator):迭代器定义访问和遍历元素的接口。
  • 具体迭代器(ConcreteIterator):具体迭代器实现迭代器接口,并保存迭代过程游标的位置。
  • 聚合(Aggregate):聚合定义创建相应迭代器对象的接口。
  • 具体聚合(ConcreteAggregate):具体聚合实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例。

功能

  • 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。
  • 使用next()获得序列中的下一个元素。
  • 使用hasNext()检查序列中是否还有元素。
  • 使用remove()将迭代器新返回的元素删除。

 

分类:

  • 内部迭代器:是将迭代器的操作直接添加到实现ADT的类中得到的。这样做的好处就是可以直接访问ADT类中的数据。所以效率很高,但是这样也有一个缺点。那就是同一个时刻只能有一个迭代器。
  • 外部迭代器:是一个独立于ADT类而实现的类的对象。。外部迭代器只能通过ADT类所提供的共有 接口来访问ADT的数据,所以这样效率底下。但是这样可以同一个时刻有多个迭代器。。也就是说可 以同时对同一个数据集有多个迭代器。
  • 内部类迭代器:顾名思义就是把迭代器类封装到ADT类中,而ADT类提供一个对外的接口,这样可以让客户程序员得到这个内部类迭代器的对象。内部类迭代器的好处就是高效而且可以同一个时刻有 多个迭代器。说它高效是因为是内部类,可以直接访问ADT的私有数据。JAVA中的迭代器是内部 类迭代器。

实现:

  • 一般要操作的容器有支持的接口才可以
  • 在迭代器遍历的过程中,通过该迭代器进行容器元素的增减操作是否安全呢?
  • 在容器中存在复合对象的情况,迭代器怎样才能支持深层遍历和多种遍历呢?

适用范围:

  • 访问一个容器对象的内容而无需暴露它的内部表示。
  • 支持对容器对象的多种遍历。
  • 为遍历不同的容器结构提供一个统一的接口(多态迭代)。

 

中介者模式:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它 们之间的交互。简单点来说,将原来两个直接引用或者依赖的对象拆开,在中间加入一个"中介"对象,使得两头的对象分别和"中介"对象引用或者依赖。由于中介者的行为与要使用的数据与具体业务紧密相关,抽象中介者角色提供一个能方便很多对象使用的接口是不太现实的。所以抽象中介者角色往往是不存在 的,或者只是一个标示接口。如果有幸能够提炼出真正带有行为的抽象中介者角色,我想同事角色对具体中介者角色的选择也是策略的一种应用。

意图:用一个中介对象来封装一系列的对象交互。

角色及职责:

  • 抽象中介者(Mediator)角色:抽象中介者角色定义统一的接口用于各同事角色之间的通信。
  • 具体中介者(Concrete Mediator)角色:具体中介者角色通过协调各同事角色实现协作行为。为此它要知道并引用各个同事角色。
  • 同事(Colleague)角色:每一个同事角色都知道对应的具体中介者角色,而且与其他的同事角色通信的时候,一定要通过中介者角色协作。

中介者模式和外观模式的对比:

  • 中介者模式解决的是多个对象之间的通信问题,减少类之间的关联外观模式解决的是子系统的接口复杂度问题
  • 中介者模式中对象可以向中介者请求,外观模式中对象不会对外观有任何协作请求。

优点:

  • 减少子类的生成   Mediator将原本分布于多个对象间的行为集中在一起。改变这些行为只需生成Mediator的子类即可。
  • 它将各Colleague解耦  Mediator有利于各Colleague类间的松耦合。可以独立的改变和复用各Colleague类和Mediator类。
  • 它简化了对象协议   用Mediator和各Colleague间的一对多的交互来替代多对多的交互。一对多的关系更易于理解。维护和扩展。
  • 它对对象如何协作进行了抽象   将中介作为一个独立的概念并将其封装在一个对象中,将注意力从对象各自本身的行为转移到他们之间的交互上来。这有助于弄清楚一个系统中的对象是如何交互的。

缺点:

  • 它使控制集中化   中介者模式将交互的复杂性变为中介者的复杂性。因为中介者封装了协议,它可能比任何一个Colleague都复杂。这可能使得中介者自身成为一个难于维护的庞然大物。

 

备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态

角色及职责:

  • 备忘录(Memento)角色:备忘录角色存储"备忘发起角色"的内部状态。"备忘发起角色"根据需要决定备忘录角色存储"备忘发起角色"的哪些内部状态。为了防止"备忘发起角色"以外的其他对象访问备忘录。备忘录实际上有两个接口,"备忘录管理者角色"只能看到备忘录提供的窄接口,对于备忘录角色中存放的属性是不可见的。"备忘发起角色"则能够看到一个宽接口,能够得到自己放入备忘录角色中属性。
  • 备忘发起(Originator)角色:"备忘发起角色"创建一个备忘录,用以记录当前时刻它的内部状态。在需要时使用备忘录恢复内部状态。
  • 备忘录管理者(Caretaker)角色:负责保存好备忘录。不能对备忘录的内容进行操作或检查。

    优点:使用备忘录模式,可以避免暴露一些只应由源发器管理却又必须存储在源发器之外的信息,而且能够在对象需要时恢复到先前的状态。

    缺点:使用备忘录可能代价很高。如果源发器在生成备忘录时必须复制并存储大量的信息,或者客户非常频繁地创建备忘录和恢复源发器状态,可能会导致非常大的开销。

适用范围:主要搭配命令方式使用。

 

观察者模式:定义了一种把改动通知给多个对象的方式。

意图:降观察着雨被观察者完美的分开。

角色及职责:

  • 抽象主题(Subject)角色:主题角色把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,主题角色又叫做抽象被观察者(Observable)角色,一般用一个抽象类或者一个接口实现。具有三个方法接口:attach增加观察者到串联链内、detach从串联链内删除观察者、notify通知目标发生变化。
  • 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者角色一般用一个抽象类或者一个接口实现。在这个示意性的实现中,更新接口只包含一个方法(即Update()方法),这个方法叫做更新方法
  • 具体主题(ConcreteSubject)角色:将有关状态存入具体现察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者角色(Concrete Observable)。具体主题角色通常用一个具体子类实现。实现接口getstate,返回状态信息。
  • 具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体现察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体现察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。当这个函式被目标物件呼叫时,观察者物件将会呼叫目标物件的取得状态函式,来其所拥有的更新目标物件资讯。 每个观察者类别都要实做它自己的更新函式,以应对状态更新的情形。

适用范围:

  • 当抽象个体有两个互相依赖的层面时。封装这些层面在单独的物件内将可允许程式设计师单独地去变更与重复使用这些物件,而不会产生两者之间交互的问题。
  • 当其中一个物件的变更会影响其他物件,却又不知道多少物件必须被同时变更时。
  • 当物件应该有能力通知其他物件,又不应该知道其他物件的实做细节时。
  • 观察者模式通常与 MVC 范式有关系。在 MVC 中,观察者模式被用来降低 model 与 view 的耦合程度。一般而言, model 的改变会触发通知其他身为观察者的 model 。而这些 model 实际上是 view 。

 

状态模式:允许一个对象在其内部状态改变时修改它的行为。

意图:

角色及职责:

  • 使用环境(Context)角色:客户程序是通过它来满足自己的需求。它定义了客户程序需要的接口;并且维护一个具体状态角色的实例,这个实例来决定当前的状态。
  • 状态(State)角色:定义一个接口以封装与使用环境角色的一个特定状态相关的行为。
  • 具体状态(Concrete State)角色:实现状态角色定义的接口。

实现:

  • 一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
  • 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。

 

策略模式:将算法封装到类中。定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

意图:使得类独立于客户而存在。在运行时根据需要透明地更改对象的算法,将算法与对象本身解耦。

角色及职责:

  • Context(应用场景): 需要使用ConcreteStrategy提供的算法。内部维护一个Strategy的实例。负责动态设置运行时Strategy具体的实现算法。负责跟Strategy之间的交互和数据传递。
  • Strategy(抽象策略类):定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。
  • ConcreteStrategy(具体策略类):实现了Strategy定义的接口,提供具体的算法实现。

适用范围:

  • 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。(例如FlyBehavior和QuackBehavior)
  • 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。(例如FlyBehavior和QuackBehavior的具体实现可任意变化或扩充)
  • 对客户(Duck)隐藏具体策略(算法)的实现细节,彼此完全独立。

优点:

  • 提供了一种替代继承的方法,而且既保持了继承的优点(代码重用)还比继承更灵活(算法独立,可以任意扩展)。
  • 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。
  • 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。

缺点:

  • 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。

 

 

模板方法模式:提供算法的一个抽象定义。在一个方法里定义算法的骨架,将一些步骤延迟到其子类。

意图:

角色及职责:

  • 抽象基类(AbstractClass):主要是定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。这有点像C语言中的一个"钩子(hook";同时将公用的代码移植到TemplateMethod中,实现的代码的公用。所以TemplateMethod一般是一个具体的算法。模版方法应该final修饰,保证派生类没有修改扩展的权限。
  • 具体实现子类(ConcreteClass):实现抽象基类的一个或多个接口,负责算法的具体实现。

 

访问者模式:在不改变类的前提下,为一个类添加多种操作。(最常见的操作模式,KJ70必须要用到)

意图:将算法与对象结构分离。

基本思想:先我们拥有一个由许多对象构成的对象结构,这些对象的类都拥有一个accept方法用来接受访问者对象;访问者是一个接口,它拥有一个visit方法,这个方法对访问到的对象结构中不同类型的元素做出不同的反应;在对象结构的一次访问过程中,我们遍历整个对象结构,对每一个元素都实施accept方法,在每一个元素的accept方法中回调访问者的visit方法,从而使访问者得以处理对象结构的每一个元素。我们可以针对对象结构设计不同的实在的访问者类来完成不同的操作。访问者模式使得我们可以在传统的单分派语言(如Smalltalk、Java和C++)中模拟双分派技术。对于支持多分派的语言(如CLOS),访问者模式已经内置于语言特性之中了,从而不再重要。

角色及职责:

  • Visitor访问者抽象接口:通过visit(Element)方法访问Element(数据结构),完成对Element的操作行为。
  • ConcreteVisitor访问者的具体实现类。
  • ObjectStructure复合对象。包括所有需要被访问的数据结构对象Element 。ObjectStructure本身也可以作为被访问者。Element元素,也就是被访问者,通过accept(Visitor)方法接受Visitor的访问。
  • ConcreteElementElement的具体实现类、

适用范围:

预料到对于基类有可能添加新的操作,但无法预料具体操作种类。

 

对象池模式:一个对象池是一组已经初始化的可以使用的对象, 而不需要在需要时创建和销毁. 池的客户从池中获取一个对象, 并对其进行操作. 客户使用完对象后, 把对象交还给池, 而不是销毁它. 这是一种特殊的工厂对象. 在初始化类实例代价高, 经常实例化类, 一次实例化数少的情况下, 对象池能带来显著的性能提升. 从池中取对象的时间是可预测的, 而新建一个对象所需的时间是不确定的

posted on 2010-03-01 16:25  BLoodMaster  阅读(3735)  评论(0编辑  收藏  举报