再读GOF设计模式-行为模式

下面2个模式目的都是将请求的发送者和接受者解耦,这样无论请求最终被哪个接受者处理,或者有多少个接受者在等待请求,都不会影响发送者。具体每种模式都有自己特定的适用场景,可以细细体会差别。

职责链模式

和装饰器模式很相似,也是通过链式构造出一个对象链条,所有对象都实现同一个接口,并且每个对象内维护另外一个对象。

将一个链条的头部对象交给使用者,那么使用者会调用它的处理方法,如果当前对象无法处理请求那么就把请求交给链条的下一个对象处理。

为了减少重复代码,需要将共同的行为:保存下一个对象和处理方法(默认行为是调用链条下一个对象)这两个行为提到一个抽象基类里,链条上的对象都应该继承这个抽象基类,并覆盖处理方法,当请求无法处理时交给抽象基类,它的默认行为是把请求传递给链条下一个对象。

通常,职责链可以用在组合模式中,请求的传递是依靠组合对象的parent向上传递的,这个在javascript里的事件捕捉体现的淋漓尽致。

命令模式

这个模式也是用来解耦事件的发送者和接受者的,之前职责链强调的是请求可能被若干处理者之一处理,而命令模式仅仅强调将接收者隐藏起来。一般会为每个发送者绑定一个的命令对象,命令对象可以保存命令执行历史,从而能够支持撤销命令。

首先需要抽象一个命令基类,任何具体命令继承它并覆写处理方法,其构造函数通常传入一个命令的接收者,命令对象自身则由发送者保存。当事件发生时,发送者调用命令对象的处理方法,命令对象最终将请求发给接收者,这样就将接收者藏到了命令对象内部,对发送者来说只认识命令抽象对象即可。

命令模式除了可以执行处理方法(execute),还可以支持撤销(unexecute)方法,这是因为命令对象可以保存操作的历史记录,因此实现撤销也不会太麻烦。

解释器模式

忽略

迭代器模式

如果我们有很多容器,它们有的是哈希表,有的是链表,有的是跳表,存储的都是Book对象,如果我想遍历它们存储的Book对象,每种数据结构都不同,那么就要写多套代码去遍历不同的数据结构,迭代器模式就是抽象这个问题的。

迭代器定义一个抽象基类,它提供first(),next(),isDone(),currentItem()等抽象方法,具体每个容器继承实现自己的迭代器类,这样用户只需要访问迭代器抽象对象的这些方法顺序的遍历集合既可,迭代器对象通常需要由具体的容器对象创建,因为迭代器的实现需要依赖具体容器对象内部的数据结构,因此通常还会抽象另一个基类,容器需要继承并覆写createIterator方法,它返回一个用于访问该容器对象的迭代器对象。

上述迭代器用法也称为外部迭代器,也就是用户直接操作迭代器进行遍历。相对的就有一种叫做内部迭代器,它一般抽象一个Traverser基类,它保存一个抽象迭代器对象并提供一个遍历方法:它调用保存的迭代器对象的first,next…等方法逐个访问元素,用户要访问这些元素的话需要继承Traverser基类并覆写一个访问方法,遍历方法会将每个元素传给我们的访问方法,我可以在在不触及迭代器接口的的情况下访问到每个元素。

同时要注意,对于哈希表这一种数据结构,虽然它的迭代器遍历算法固定的,但是它存储的对象类型可能不是Book,因此在编译型语言(C++)中迭代器需要结合模板技术(template)实现元素类型的编译时变化。

 中介者模式

一般跨部门合作,直接和多个部门沟通效率太低屁事也比较多,中介者模式是说由一个统筹者来作为中介,部门间不要直接沟通而是通过中介者沟通,由中介者把控可以实现高效的沟通。

中介者模式的目的就是把耦合降低,本来需要和N个部门沟通,现在只需要和中介者沟通,中介者可以实现复杂的跨部门沟通逻辑,这能够提升跨部门的沟通效率。因此我们基本可以得知,中介者是一个类,中介者需要知道各个部门的负责人,同时各部门负责人需要认识中介者。

中介者模式和外观模式很像,都是用一个大对象把各个系统封装起来,这样降低使用者对各子系统的耦合度。只不过中介者强调的是各个子系统之间通过中介者互相沟通,而外观模式是把各个系统隐藏起来,暴露一些整合过的功能给外部用户使用。

备忘录模式

用来备份一个对象的内部状态,需要给待备份的对象增加2个方法,一个是生成备份,一个是恢复备份。

生成的备份对象,则由使用者自己保存,如果需要恢复备份则调用之前那个待备份对象的恢复方法,将备份对象传给它从而恢复到此前的状态,如果不需要恢复则可以随时删除掉备份对象。

观察者模式

一个目标对象可以被N个对象观察,当这个目标对象发生任何改变就通知这些观察者。

因此,需要对观察者抽象一个基类,不同的观察者继承实现自己的观察逻辑,另外目标对象能同时被多个对象观察,因此需要抽象一个目标对象基类,它提供添加观察者,删除观察者,通知观察者三个方法实现,提供默认行为。

观察者模式又分推模型和拉模型,所谓推就是目标对象知道观察者需要哪些数据,当发生改变时把数据作为参数告知它们。而拉模型则仅仅是通知观察者发生了变动,具体内容需要观察者自己来主动询问目标对象。

最后,一个观察者对象可以观察多个对象,因此在定义观察者类的通知接口时,可以指定接受一个参数来表明是哪个目标对象发来的通知。

状态模式

这个模式用来做状态机比较适合,当从一个状态变成另一个状态时,管理者不再关注状态切换的细节,而是由状态对象独自决定。

以往实现状态机,通常我们会在管理者类种,用switch case这种结构来判断当前处于什么状态,然后进行对应的逻辑处理,并切换到下一个状态,这样做的缺点就是不断的膨胀的代码,并且所有逻辑都实现在这个类中。

利用状态模式,可以抽象一个状态基类,每个业务状态需要继承它并覆写处理方法。管理对象初始化只保存一个初始状态对象,当程序运行时调用这个状态对象的处理方法,在处理方法种实现业务逻辑,并创建代表下一个状态的对象设置到管理对象中去,从而完成状态的转换。

这样做的好处,就是扩展性是独立于业务对象之外的,逻辑都实现在状态对象内,并且状态的变化对于业务对象都是透明的。另外,业务对象一般要把自身作为参数传递给状态对象,这样状态对象可以调用业务对象获取需要的数据,以及为业务对象设置新的状态。

策略模式

通常用来动态的替换算法实现,做法是抽象一个策略类并继承实现不同的策略实现,将策略对象作为参数传给业务对象,从而实现算法的替换。

模板方法模式

这个和策略模式的目标类似,都是替换算法部分。

首先定义一个模板基类,它实现了一些非抽象的逻辑代码(非抽象方法),在其内部调用了个别抽象方法,这些抽象方法可以灵活替换实现。

这就要求我们继承基类,覆写抽象方法,从而实现算法的局部替换,而此前策略模式是通过传入策略对象的方式实现的。

访问者模式

假设有几个类对象,它们的个别方法经常改变,为了减少对这些类的经常修改,可以将这些方法的实现提出来,放到访问者对象中实现。

访问者要有一个抽象基类,它为这些多变的方法定义抽象接口,不同的访问者可以继承实现这些接口。

接下来,我们将之前的类的方法改为接受1个访问者对象为参数,在方法内部将this交给访问者,这样原有的代码逻辑就可以在访问者对象中实现了。

发表评论

电子邮件地址不会被公开。