设计模式之行为型模式

Deadly 提交于 2019-12-17 19:19:58

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

我们在前面已讨论了12种设计模式。其中涉及的是:变与不变,对变化如何处理,以及调用关系,对不可调用,如何方便实现成为可以调用。剩下的11个模式,都是与行为传递有关的,即不是变与不变的问题,也不是调用关系问题。而是调用流程控制的问题。这是因为,我们使用了类,使用了SOLID原则,调用流程不再是if else 或 switch case。 处理好这样的流程控制,则更易于我们的代码的简化。

Chain of Responsibility(责任链):为解除请求的发送者和接受者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。
这样的做法,使得每一个类中的流程控制得以统一。并且,可以将这样的流程控制封装到对应的类中,而不是由调用者来处理,因而调用者不再需要了解这个链中有多少个程员,该按怎样的顺序调用。从而丢开了调用者与被调用者之间的紧密耦合。
实现方式:被调用者作为具体代码,其类中增加$next属性,保存下一个有责任的类。被调的方法中,判断,有没有$next,如果有,则调用$next中的同名方法。
总结:一次性同时调用多个类中的同一个方法。(类方法递归)
选择模式:通过类的链,实现方法选择的组合。

Command(命令):将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
实现方式:把对于一个类的方法的调用,封装成命令对象。调用时,不直接调用目标类的方法,而是调用命令对象。通过命令对象决定调用哪一个目标类中的哪一个目标方法。由此,调用者只与统一的命令对象接触,而不需要了解目标类的方法与细节。这样,使得目录类是可变的,并且,调用是可以加以限制的,比如:延时,撤消等。
抽象的命令对象,定义命令的统计接口。命令对象中的构造函数的参数是命令的接收者。实际的命令通过继承命令对象来实现,从而实现抽象命令对象中的execute方法,或cancel方法,保证命令执行或取消。
总结:命令对象的目标类可变,并且,调用类永远使用不变的命令。不管被调用的类中的方法是否变化。
选择模式:类中的方法。

Interpreter(解释器):给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
解释器的根本目的,是处理多样的复杂指令,并且这些指令原本是散落在程序不同地方的。通过解释器,我们只需要一套这样的程序就够了,从而把散落在到处的算法给集中了起来。
也许你认为,程序既然可以集中,那么,散落在各处的指令直接使用对象的方法调用不是一样吗?你错了。这样做调用方式就反了。解释器仍是核心调用具体。而不是让具体调用核心。如果是具体调用核心,那解释器就不需要了。
实现方式:可以通过不同的语法节点的解释器聚合实现目标解释器类。
总结:解释器是对输入的预处理,也可以用作输出的后处理。使其转换为程序的可用形式。
选择模式:数据选择。

Iterator(迭代器):提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
实现方式:使用一个迭代器类,实现getCurrentX与getNextX方法。其中X是指你要访问的具体的数据部分。
最简单的,PHP已经为我们封装好了。那就是PHP对象中的属性,我们可以用FOREACH进行访问,同时,如果我们使用SPL的ARRAYACCESS接口,也就可以用顺序访问。顺序访问使程序变得简单统一,并且调用者不再需要了解更多的细节。
现实中也有很多这样类似的需求。比如工资表的打印,实际上是把查询结果中的一组记录要按同一员工ID变成一行记录。这时,如果用简单的FRO循环是不行的,而使用Iterator(迭代器),这样的工作就被封装到了类的内部。
总结:通过一个类实现对复杂结构中某一项的顺序访问,实现封装,同时简化调用方的操作。
选择模式:数据选择。

Mediator(中介者):用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
实现方式:创建一个Mediator类,保证当A与B双方都不确定时,运行时确定的A能直接访问Mediator,运行时确定的B能直接访问Mediator,这样即实现了任一A与任一B的互相操作。
所以,作为Mediator类,要能够允许用户确定或变更调用方A,或被调用方B。并且,Mediator类负责将调用方A的调用传递给被调用方B。
总结:Mediator不需要你知道你要访问的目标类及其方法是哪一个。
选择模式:类及其方法选择。

Memento(备忘录):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到保存的状态。
实现方式:另建一个类,类名可以实际状态的名称,比如:BookMark表示书签,记录用户将此书读到了哪一页。
Memento(备忘录)的根本目标是实现可撤消的变更,通过Memento类将对应类的数据或操作变更记录下来。这样,如果需要撤消,可以在Memento回朔寻找,从而撤消到指定的操作之前。
总结:记住对象的的某一状态,以便可以回朔。
选择模式:状态选择

Observer(观察者):定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
实现方式:把对多个对象的更新操作封装到Observer的一个方法中。在Observer的方法中,我们可以根据不同的情况,决定需要同时更新哪些类。这即是具体的算法了。由此可见,这里,核心与具体的关系是,核心是调用与被调用方的类,而具体的是数据更新算法,如何更新,更新哪些类,则是要根据不同的情况决定的。
总结:给调用方提供单一方法,方便操作。
选择模式:方法选择

State(状态):允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。
状态对于程序而言,实际上可以算是全局变量。但它是一些特定的类所用的参数。假如我们作为参数传入,则有一个问题,那就是调用者从哪里得到这个参数?如果没有合法的途径得到,那么,这个参数仍要放到全局变量中。而被调用者的行为,则是依的据这此参数而改变的。所以,用一个状态类保存这些参数,这样,被调用者就能得到这些参数。从而能按当前状态而改变。
实现方式:创建状态基本对象,由它决定在不同状态下使用不同的具体状态对象。
总结:State通过状态的改变切换对象。
选择模式:根据状态选择对象。

Strategy(策略):定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。
实现方式:定义好策略接口或父类,相关子类中实现具体的算法。调用时,根据参数创建不同的策略子类。
总结:给调用方提供单一接口,方便操作。
选择模式:通过子类选择,实现不同方法也就是不同算法的选择

Template Method(模板方法):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
实现方式:定义模板父类,子类实现不同步骤的算法具体的情况好比:如果要完成一个操作,需要多个步骤,比如:A,B,C。但在不同的情况下或对应不同的类,这三个步骤中的算法也是不相同的。那么,这就属于具体变化的代码,为此,我们需要定义一个接口或抽象类,在其中有A,B,C三个方法的定义,而后,根据不同情况派生不同的具体的类,从中具体实现算法的细节。
总结:模板模式可以将一系列的步骤自动化,同时又可以满足不同的细节变化。
选择模式:通过特定的模板子类,选择特定操作步骤中的特定算法。

Visitor:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
当面对某一个对象,该对象可能拥有复杂的结构,即有可能是有很多元素(类)组成的。而程序中存在千变万化的很多对对象主体或元素的各类操作。假如把这些具体的所有的操作都写到对应的类中,那么,就破坏了原对象结构的纯洁性,产生了污染。特别是,如果是当前程序的当前模块的操作,可能较少,如果加上其它模块的,甚至包括共享给其它应用程序的。
在这一情况下,把这一类操作进行封装,从而就不需要将这些操作添加到对应的类中,成为对应类的方法。而对于不同的调用方,他们只了解他们自己的Visitor类,只使用他们用需要的操作。
实现方式:首先提供对整体或元素的一致的访问接口,比如:父类实现对整体的访问,对于PHP,子类用“伪重载”方法实现对不同元素的访问,当然也可以继承父类实现具体的操作。其次是,访问者类中实现调用方所需的对目标类的操作。
总结:对整体与单个元素的访问接口统一
选择模式:对象结构中的元素选择

后记:现在你己了解了报有23种设计模式了。也许,你认为,这些没有“例题”的讲解,你无法写出对应的代码,那么,你可以先看看PHP文档中关于PHP面向对象的章节,其次,你可以参考一下网站:http://www.fluffycat.com/PHP-Design-Patterns/,这里有所有PHP设计模式的示例原码。注意,记住所有23个设计模式的名称是比较重要的。因为,他不只是专业术语,也是你领导程序开发中的暗号。如果你上司叫你,这个问题用工厂模式解决,如果你不能记住这些名称,那么,你就无法了解你上司的意图。你做上司也一样,你可以用一个名称告诉你的下属,如何处理面临的问题。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!