行为型模式总结
行为型模式(Behavioral Pattern)是对在不同的对象之间划分责任和算法的抽象化。行为型模式不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。通过行为型模式,可以更加清晰地划分类与对象的职责,并研究系统在运行时实例对象 之间的交互。在系统运行时,对象并不是孤立的,它们可以通过相互通信与协作完成某些复杂功能,一个对象在运行时也将影响到其他对象的运行。
行为型模式分为类行为型模式和对象行为型模式两种:
类行为型模式:类的行为型模式使用继承关系在几个类之间分配行为,类行为型模式主要通过多态等方式来分配父类与子类的职责。
对象行为型模式:对象的行为型模式则使用对象的聚合关联关系来分配行为,对象行为型模式主要是通过对象关联等方式来分配两个或多个类的职责。根据“合成复用原则”,系统中要尽量使用关联关系来取代继承关系,因此大部分行为型设计模式都属于对象行为型设计模式。
常见的行为型模式有11种:
Template Method模版方法、Command命令、Interpreter解释器、Mediator中介者、Iterator迭代器、Observer观察者、Chain of Responsibility职责链、Memento备忘录、State状态、Strategy策略、Visitor访问者。
1.模版方法模式(Template Method)
定义一个操作中算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变算法的结构即可重定义该算法的某些特定的步骤。模版方法是一种基本代码复用技术,在类库中尤为重要,它们提取了类库中的公共行为。算法的结构可理解为根据需求设计出来的业务流程,特定的步骤就是指那些可能在内容上存在变数的环节。
优点:模板方法模式在一个类中形式化地定义算法,而由它的子类实现细节的处理。模板方法模式是一种代码复用的基本技术。模板方法模式导致一种反向的控制结构,通过一个父类调用其子类的操作,通过对子类的扩展增加新的行为,符合“开闭原则”。
缺点:每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,但是更加符合“单一职责原则”,使得类的内聚性得以提高。
协作:ConcreteClass靠AbstractClass来实现算法中不变的步骤。
参与者:
(1)AbstractClass(抽象类):定义一个模版方法,定义一个算法的骨架;定义抽象的原语操作,具体的子类将重定义它们以实现算法的各个步骤。该模版方法不仅调用原语操作,也调用定义在AbstractClass或其它对象中的操作。
(2)ConcreteClass(具体类):实现原语操作以完成算法中子类相关的特定步骤。
2.命令模式(Command)
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。别名动作(Action),事务(Transaction)。和很多设计模式一样,命令模式在你的请求和处理之间加上了一个中间人的角色,来达到分离耦合的目的。
优点:降低系统的耦合度。新的命令可以很容易地加入到系统中。可以比较容易地设计一个命令队列和宏命令(组合命令)。可以方便地实现对请求的Undo和Redo。
缺点:使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。
协作: Client创建一个具体的ConcreteCommand对象并指定它的Receiver对象;某Invoker对象存储该ConcreteCommand对象,通过调用Command对象的Execute操作来提交一个请求,若该命令是可撤销的,ConcreteCommand就在执行Execute操作之前存储当前状态以用于取消该命令;ConcreteCommand对象调用它的Receiver的一些操作以执行该请求。
参与者:
(1)命令角色(Command):声明执行操作的接口。
(2)具体命令角色(ConcreteCommand):讲一个接受者对象绑定一个动作;调用接收者相应的操作,以实现命令角色声明的执行操作的接口。
(3)客户角色(Client): 创建一个具体命令对象(并可以设定它的接收者)。
(4)请求者角色(Invoker):调用命令对象执行请求。
(5)接收者角色(Receiver):知道如何实施与执行一个请求相关的操作。任何类都可能做一个接收者。
3.解释器模式(Interpreter)
定义语言(使用规定格式和语法的代码)的文法,并且建立一个解释器来解释该语言中的句子。它属于类行为模式。解释器模式提供一种简单方法来执行语法且容易修改和扩展。一般系统中很多类使用相似的语法,可以使用一个解释器来代替每一个规则实现一个解释器。在解释器中不同规则是由不同类实现的,从而使添加一个新的语法规则简单化,但对于复杂语法难以维护。
4.中介者模式(Mediator)
用一个中介对象封装一系列的对象交互。中介者使各对象间不需要显式的相互引用,从而使其松散耦合,而起可以独立的改变它们之间的交互。
优点:简化了对象之间的交互。将各同事解耦。减少子类生成。可以简化各同事类的设计和实现。
缺点:在具体中介者类中包含了同事之间的交互细节,可能会导致具体中介者类非常复杂,使得系统难以维护。
协作:同事向一个中介者发送和接收请求。中介者在各同事间适当的转发请求以实现协作行为。
参与者:
(1)Mediator(中介者):定义一个接口用于和同事对象间通信。
(2)ConcreteMediator(具体中介者):协调各同事对象对象实现协作行为;了解并维护它的各个同事。
(3)Colleague(同事角色):每一个同事角色都知道它的中介者对象,在需要与其它同事通信时,与它的中介者通信。
6.迭代器模式(Iterator)
提供一种方法顺序访问一个集合中各个元素,而又不暴露该对象的内部表示。别名:游标(Cursor)。
7.观察者模式(Observer)
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并自动更新。别名:Dependents(依赖),发布-订阅(Publish-subscribe)
协作:当ConcreteSubject发生任何可能导致其观察者与其本身状态不一致的改变时,它将通知各个观察者。在得到一个具体目标的改变通知后,ConcreteObserver可向目标对象查询信息,从而使它的状态与目标对象状态保持一致。
参与者:
(1)Subject(抽象目标角色):提供注册和删除观察者对象的接口,知道它的观察者。可以有任意多个观察者观察同一个目标。
(2)Observer(观察者角色):为那些在目标发生改变时需要更新的对象定义一个更新接口。
(3)ConcreteSubject(具体目标):将有关状态存入ConcreteObserver对象,当它的状态发生改变时,向它的各个观察者发出通知。
(4)ConcreteObserver(具体观察者):维护一个指向ConcreteSubject对象的引用,存储有关状态,这些状态应与目标状态保持一致,实现Observer的更新接口,以使自己状态与目标状态保持一致。
8.职责链模式(Chain of Responsibility)
将对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。职责链可简化对象的相互连接,它们仅需保持一个指向后继者的引用,而不需要保持所有候选者的引用,链中对象不用知道链的结构,接受者和发送者都没有对方的明确信息。可以通过在运行时刻动态的增加或修改链来动态的改变处理一个请求的职责;不能保证请求一定被接受,一个请求也可能因链没有被正确配置而得不到处理。
优点:降低耦合度,可简化对象的相互连接,增强给对象指派职责的灵活性,增加新的请求处理类很方便
缺点:不能保证请求一定被接收。系统性能将受到一定影响,而且在进行代码调试时不太方便;可能会造成循环调用。
协作:当客户提交一个请求时,请求沿着链传递直至有一个ConcreteHandler对象负责处理它或者到链末。
参与者:
(1)Handler:定义一个实现请求的接口。
(2)ConcreteHandler:处理它负责的请求,可访问它的后继者;如果可处理请求就处理,否则将请求转发给它的后后继者。
(3)Client:向链上的具体处理者对象提交请求。
9.备忘录模式(Memento)
在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以后可将该对象恢复到原先保存的状态。别名:Token。
优点:提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用先前存储起来的备忘录将状态复原。
实现了信息的封装,一个备忘录对象是一种原发器对象的表示,不会被其他代码改动,这种模式简化了原发器对象,备忘录只保存原发器的状态,采用堆栈来存储备忘录对象可以实现多次撤销操作,可以通过在负责人中定义集合对象来存储多个备忘录。
缺点:资源消耗过大,如果类的成员变量太多,就不可避免占用大量的内存,而且每保存一次对象的状态都需要消耗内存资源,如果知道这一点大家就容易理解为什么一些提供了撤销功能的软件在运行时所需的内存和硬盘空间比较大了。
协作:管理器向原发器请求一个备忘录,保留一段时间后将其返回给原发器。备忘录是被动的,只有创建备忘录的原发器会对它的状态进行赋值和检索。
参与者:
(1)Memento(备忘录角色):存储备忘发起角色的内部状态,备忘发起角色根据需要决定备忘录角色存储自己哪些内部状态。为了防止备忘发起角色以往的对象访问备忘录,管理者只能看到备忘录的窄接口——它只能把备忘录传递给其它对象,原备忘发起角色可以看到一个宽接口,允许它访问返回到先前状态所需的所有数据。
(2)Originator(原发器,备忘发起角色):创建一个备忘录,用于记录当前时刻它的内部状态,在需要时使用备忘录恢复内部状态。
(3)Caretaker(管理器,备忘录管理者角色):负责管理备忘录,不能对备忘录的内容进行操作或检查。
10.状态模式(State)
允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。别名:状态对象。
协作:Context将与状态相关的请求委托给当前的ConcreteState对象处理,也可将自身作为一个参数传递传递给以处理该请求的状态对象,从而使状态对象在必要时可访问Context。Context是客户使用的主要接口,客户可用状态对象来配置一个Context,一旦Context配置完毕,它的客户不再需要直接与状态对象打交道。Context和ConcreteState子类都可决定哪个状态时另外哪一个状态的后继者,以及在何种条件下进行状态转换。
参与者:
(1)Context(环境):维护一个ConcreteState实例,这个实例定义当前状态。
(2)State(状态):定义一个接口以封装与Context的一个特定状态相关的行为。
(3)ConcreteState(具体状态):实现一个与Context的一个状态相关的行为。
11.策略模式(Strategy)
定义一系列算法,把它们一个个封装起来,并且使它们可相互替换,本模式使得算法可独立于使用它的客户而变化。别名:政策(policy)。
优点:策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。策略模式提供了管理相关的算法族的办法。策略模式提供了可以替换继承关系的办法。使用策略模式可以避免使用多重条件转移语句。
缺点:客户端必须知道所有的策略类,并自行决定使用哪一个策略类。策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。
协作:Strategy和Context相互作用以实现选定的算法。当算法被调用时,Context可将该算法所需要的所有数据都传递给Strategy,或者Context可将自身作为一个参数传递给Strategy操作,从而使Strategy在需要时可以回调Context。Context将它的客户请求转发给它的Strategy,客户通常创建并传递一个ConcreteStrategy对象给该Context;这样客户仅与Context交互。通常有一系列ConcreteStrategy类可供客户从中选择。
参与者:
(1)Strategy(策略):定义所有支持算法的公共接口。Context使用这个接口来调用某个Concretestrategy定义的算法。
(2)ConcreteStrategy(具体策略):以Strategy为接口实现某具体算法。
(3)Context(上下文):维护一个Strategy对象的引用,用一个ConcreteStrategy来配置,可定义一个接口来让Strategy访问它的书籍。
12.访问者模式(Visitor)
表示以作用于对象结构中各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
优点:使得增加新的访问操作变得很容易。将有关元素对象的访问行为集中到一个访问者对象中,而不是分散到一个个的元素类中。可以跨过类的等级结构访问属于不同的等级结构的元素类。让用户能够在不修改现有类层次结构的情况下,定义该类层次结构的操作
缺点:增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求。破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。
协作:一个使用Visitor模式的客户必须创建一个ConcreteVisitor对象,然后遍历该对象结构,并用该访问者访问每一个元素。当一个元素被访问时它调用对应它的类的Visitor操作,如有必要该元素将自身作为操作的一个参数以便该访问者访问它的状态。
参与者:
(1)Visitor(访问者):为该对象结构中每一个ConcreteElement声明一个Visit操作,该操作的名称和特征标志了发送Visit请求给该标识者的那个类,使访问者可以确定正被访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。
(2)ConcreteVisitor(具体访问者):实现每个Visitor声明的操作。每个操作实现本算法的一部分,而该算法片段乃是对应于结构中对象的类。ConcreteVisitor为该算法提供了上下文并存储它的局部状态,这一状态常常在遍历该结构的过程中累积结果。
(3)Element(元素):定义一个Accept操作,它以一个访问者为参数。
(4)ConcreteElement(具体元素):实现Accept操作,该操作以一个访问者为参数。
(5)ObjectStructure(对象结构):可以是一个复合,提供一个高层接口以允许访问者访问它的元素,能枚举它的元素。