这段时间看了《设计模式》这本书,但看了后感觉自己对其的理解不深,所以决定写这篇博客来加深自己对于设计模式的理解。
前段时间看的一些设计模式决定等过两天在慢慢写,今天先把今天看的mediator(中介者),memento(备忘录),observer(观察者)写下来。
1,首先是mediator,我感觉它的主要作用是降低一系列互相作用的对象之间的耦合关系,因为就像蜘蛛网一样,如果对象之间有着复杂的耦合关系,就会造成管理混乱,维护和扩展也不方便。
而使用mediator模式,每个对象与其他对象之间的联系全都变成其和中介者的联系,而中介者在调用这些对象去完成所需的功能,就像以前需要记住很多人的电话,并且把每个电话打出去才能干完的活,现在只需要播个特殊号码,再将要干的事情和他说一下,他就替你把所有的电话都打了。在这种情况下只有一个人需要知道所有人的电话,也就是只有中介者需要知道所有对象并且保持联系。mediator模式下所有的对象关系都很简单,唯一复杂的只有中介者本身,但是将复杂度从所有对象身上转移到中介者一人的身上很明显是很有好处的。
在使用mediator时,需要在定义一个mediator的抽象基类(当然如果只有一个mediator既不需要了),每个对象都拥有一个mediator类的成员,要实某个功能时便通过调用这个中介者成员去和其他对象交互,而每个具体的mediator类则拥有它所需要的所有对象的实例作为成员,通过调用这些对象实例来实现对象所需要的功能。
接下来是memento(备忘录),它是为了实现撤销功能而存在的,它所起的作用就是捕获对象的内部状态,并且在对象外部保存这个状态(为什么要是在对象外部保存状态?我的理解是需要有一个外部管理者来执行撤销操作,他需要能够获取备忘录所保存的状态,而如果将这个状态保存在对象内部而又要管理者能够获取,就会破坏对象的封装性,将对象的内部成员暴露在管理者眼前),这样便可以将对象之前的动作撤销,并恢复到原来的状态。
memento模式下,原发器和管理者都需要对备忘录进行访问,但是他们所需要的访问权限是不一样的,因为原发器需要向备忘录写入或者读取状态信息,而管理者只需要简单的调用备忘录,将其提供给原发器,因此需要备忘录对原发器和管理者需要两种权限不同的接口(因为如果管理者和原发器如果对备忘录拥有一样的权限,那么管理者就能读取原发器的某些状态信息,而这种情况可能是不受欢迎的),(在c++中)那么就可以将原发器定义为备忘录的友元,将状态信息以及对状态信息的操作都定义为备忘录的privata成员,而管理者所需要的接口就定义为public成员(对其他的语言我也不太了解,应该是有差不多的解决方法的)。
memento的具体结构如下:
其中原发器和备忘录各有一个state成员,它保存原发器在某个时刻的状态,与此同时,原发器和备忘录将state信息隐藏在自身私有成员中,从而阻止将state信息暴露在管理者眼前。原发器调用setMemento和CreateMemento函数从备忘录中对state进行存取,而管理者调用原发器的setMemento接口来进行撤销动作。
3,接下来是observer(观察者模式),这种模式定义对象间的一种一对多的依赖关系,当一个对象(subject)发生变化时,所有依赖它的对象(observer)都能观察到这种变化从而自动更新。就像《设计模式》里举的例子,表,柱状图,饼状图都依赖与同一组数据,当数组发生变化时,不需要人手动去更新,这三种图都会得到数据已经更新的通知,并且自动更新自身。而且当表,柱状图或者饼状图发生变化时,数据和其他两种图也会发生更新。一个subject可以有任意数目的observer,而一个observer也可以观察多个subject。
observer模式的具体结构如下:
其中Observer为observer的抽象基类,Subject为目标基类,提供给ConcreteSubject公用的接口,其内保存有所有观察者的引用,这样在目标发生变化时通过调用这些观察者的引用来通知观察者目标已发生变化,从而调用观察者的更新。而每个ConcreteObserver也拥有一个ConcreteSubject的引用,通过此引用可以查询subject的具体的变化,也可以在自身发生变化时通知subject从而通知其他的observer进行更新。
-------------------------------------------分割线---------------------------------------------
今天看了《设计模式》中的state和strategy模式,现将我的理解写下来以加深印象。
1,state模式,在《设计模式》中,它的表述是这样的:允许一个对象在其内部状态改变是改变它的行为,使对象看起来似乎修改了它的类。我的理解是,对于一个对象来说,如果其内有一些(一个)描述其状态的成员变量发生了变化(也就意味着状态发生变化),它的某些接口的功能也会同时发生变化。所以与状态相关的接口的功能肯定是依赖表示状态的成员变量的,所以就像书里所说的,对象的接口是通过调用表示状态的成员变量(下面以state表示)的接口来实现功能的,为了能在程序运行时动态改变state,state为抽象基类,提供公共接口,通过改变不同状态(ConcreteState),来实现接口的不同功能。其实也可以使用条件语句(if,switch)来实现同样的功能,但是过多的条件语句总会使程序看起来臃肿,而且可扩展性也不好,利用state继承体系,在需要添加状态时只需要添加一个新的子类便可以了,而使用条件语句不光需要编写新的处理函数,而且还需要修改条件语句。在实现state子类时,由于子类只负责处理处理不同状态的接口,可以维持子类只有一个实例,这可以使用singleton模式来实现(当然也可以不维持只有一个实例,以实际情况决定)。
state模式的一般结构如下:
和上面说得一样,context有一个抽象基类state成员,通过在运行时调用不同的子类ConcreteState来实现状态改变时的行为改变,而状态的变化可以直接定义在context的自身接口中,也可以通过state子类来变化(因为前一个concretestate可能会对状态的切换有影响),由于图比较简单,感觉没有必要详细解释,就说这么多。
2,strategy(策略)模式,此模式是将完成同一功能的不同方法封装成同一抽象类的不同子类,从而达到在运行过程中可以方便选择不同方法实现同一功能的要求。这和state有些类似,都是用来代替条件语句的。它的结构如下:
strategy模式的结构图和state模式的结构图看起来很像,Context是我们要实现的对象的类,而strategy为抽象基类,主要用来提供接口,具体实现接口的是ConcreteStrategy子类。图上没有标出的是context必须将实现所需功能的数据传递给strategy类,而由于不同的strategy类可能需要不同的数据,所以如何选取传递的数据也就决定了此模式的扩展性,但是,context也可以将自身传递给strategy类,这样就避免了上述问题,但可能会破坏context的封装性以及加深context和strategy的耦合关系。和state模式一样,context里也包含一个strategy成员,通过调用此成员选择不同的策略。
我个人认为strategy模式和state模式结构很类似,区别就是,state是由于自身内部状态变化而使用不同的实现,而strategy是由于外部条件以及所需要实现的效果或目标不同而有权衡的选择不同的策略(由于策略是主动选择的,因此context对于每一种ConcreteStrategy都必须了解)。
--------------------------------------------分割线----------------------------------------------
今天将设计模式看完了,其实在上次看完后也就剩下了template method和visitor模式,其实看下来,我感觉template method模式没什么好说的,就是普通的多态思想(但可能是我理解错了),所以今天着重讲visitor模式。
visitor主要针对的是这样一种情况,有一系列的类,其中每种类都需要进行相同的操作,但是操作对于每种类都是不同的,相当于每种操作都对每个类进行了定制化。其实就是有n个操作和m种类,进行一个组合。以前的解决办法是对于每一种类,在类的内部构造成员函数,对于相同的操作,成员函数的名字(接口)一样,具体的实现不一样。但是这样做对于类的种类不变,而操作经常产生变化(添加操作或者删除操作)的情况很不友好,因为每添加一种操作就需要在每个类里添加一个成员函数,而一般出现的情况就是这种情况(对于操作不变化,而类本身经常发生变化的情况,这种方式其实比visitor更加合适)。
为了应付上面出现的这种情况,就出现了visitor模式,模式的具体结构如下:
其中visitor代表操作,element代表上面说的类对象。首先有个ObjectStructure,它相当于是element的集合,里面有一系列的不同的ConcreteElement。Visitor是visitor的抽象基类,提供对于每种Element的接口,而每种ConcreteVisitor实现不同的操作。在visitor模式运行过程中,对于ObjectStructure里的每种ConcreteElement,调用Element的accept函数,该函数以一个visitor作为参数,element可以选择代表自己想要进行的操作的ConcreteVisitor作为参数来作为accept的参数,在accept函数执行过程中,是调用visitor针对ConcreteElement的成员函数,element将自己作为参数,这样ConcreteVisitor便能够针对ConcreteElement进行特定操作了。在visitor模式下,如果需要添加操作的话,只需要添加一个ConcreteVisitor子类就行,变得很容易,但相反,如果需要添加ConcreteElement子类,就需要针对每种ConcreteVisitor添加相应的成员函数。所以说选择visitor模式还是将每种操作都封装成element的成员函数取决于当时所面临的具体情况,只不过visitor模式所应对的情况更加容易出现而已。
到这边已经到了《设计模式》这本书的最后一种模式,但是前面的模式我还没有将他们写进来,过两天我会将前面我没有写进来的模式都加进来,也当是一种复习了。