以下关于GOF的一些例子命名不是很准确,但是大概意思差不多,懒得再去翻书了
模拟观察者模式
模拟中介者模式
模拟command模式
模拟memento和command
模拟观察者模式
观察者与职责链模式应该是我们项目中用的最多的了
我在之前也写过一篇利用观察者模式对模块进行解耦,当时还是用纯虚函数
http://www.cnblogs.com/linyilong3/p/4232529.html
用纯虚函数来模拟接口是我们之前比较经常使用的方法
这样的缺点是 需要自己去编写接口的纯虚函数,接口的注册和反移除,接口的清理
后来发现了boost的signals库,当需要通知的时候只要负责发送信号就可以了,其他的程序只是负责connect和disconnect,signals库把我从自己编写接口管理代码中解放出来了
与观察者模式相同的是职责链模式,不同的是,职责链模式可以决定消息是否继续往下传递,而signals都会进行传递,所以如果需要职责链,可能还是需要自己去编写手动管理接口的代码
模拟中介者模式
GOF里面在对中介者模式进行介绍的时候说了设计模式中很重要的一点,就是将行为分散到各个对象中,这个也是我最近在项目代码里面所提倡的,这样的做法就是会降低可复用性
通俗点来讲就是当一个模块的子模块状态发生改变以后,如果通知其他模块或者父模块??
比如要制作图片显示的模块,当图片被放大缩小的时候,有一个文本框需要动态显示当前的比例,我们是怎么通知其他模块的呢??
在原来的代码中我们是这样的:
OnZoomIn() { parentWindow->NotifyZoomIn(); }
这样,当我们还需要通知另外一个模块A的时候,还需要依赖模块A,所以我们的画图板就依赖了parentWindow,moduleA
这个就是我们原来的编程方式,很奇怪的方式,明明画图板跟模块A和parentWindow没关系却还要去依赖他们
后来学聪明了,就改成这种方式:
1 class IPainterNotify 2 { 3 virtual void ZoomIn() = 0; 4 virtual ~IPainterNotify() = 0 5 { 6 } 7 }; 8 OnZoomIn() 9 { 10 for(all_notify) 11 { 12 notify->ZoomIn(); 13 } 14 }
这样做的麻烦之处就是我们需要自己去编写接口的管理代码,每一次都要自己定义一大堆的接口,然后自己去写register和unregister,并且经常一个类从其他模块块继承一堆interface,模块耦合很严重
后面看了QT的signal-slot机制,发现wxwidgets加上boost的signals也能做到,不过我认为boost的signals才是正道,像qt的signal需要moc支持,在编译前还需要去修改源文件,而boost的signals则是依赖function-bind机制,function和bind是用C++的模板进行实现的,属于C++自带支持
当画图板放大或缩小以后,只负责发送信号,其他模块负责提供接口,然后创建的时候互相链接就可以了,这样,各个模块之间都不知道互相之间的存在
这样就避免了GOF中间提到的面向对象会降低模块的可复用性,并且不需要自己编写接口管理,相对于书中的例子,用signals明显耦合度更低
利用function模拟command模式(命令模式)
GOF在用command模式的时候举了个例子,就是菜单栏,当我们点击菜单栏以后,要执行一系列的动作,比如openDocument,pastDocument
GOF的做法是定义一个Interface,然后将菜单的响应动作与对应的interface连接起来,这样编码很麻烦,因为我们需要
1.定义interface
2.继承interface
3.实现interface
4.链接interface
而且使得事件响应与interface产生了依赖,也使得其他模块对interface产生了依赖
我的做法是,定义一个function,然后将function与interface的事件响应bind起来,这样就不用上面那么多步骤了,我们要做的只是链接interface,然后点击菜单后,就直接调用function就可以了,而且两者并不互相依赖,
GOF还提到一个历史恢复的问题,比如画图板里面有一个剪切和粘贴功能,将一个图片粘贴后,发现不需要,需要恢复,GOF是再定义一个恢复的接口,这样,每个的command除了有excute的接口,还有一个revert的接口,编码很麻烦,因为在我们的实际项目中,如果需要状态恢复,可不是一个revert接口就够的,还需要一堆的数据,为了绑定这些数据,我们需要继承command的interface,然后传入一堆数据,然后revert的时候根据这些数据进行操作
后面我用python的closure后,想起来可以用function模拟这个操作
利用shared_ptr与function模拟memento和command模式的状态保存和恢复
与command的revert操作类似的还有memento,不过如果按照GOF上面的做法,我们又要定义一个基类,然后从这里面继承一堆子类,用function则不用那么麻烦,因为function可以绑定很多的参数
比如在画图板里面我们需要保存一个点的状态
class Point:public boost::enable_shared_from_this<Point>,public IClosure { public: Point( int x,int y ) { m_x = x; m_y = y; } virtual FUNC_CLOSURE SaveState() { auto func = boost::bind( &Point::SetPoint,this->shared_from_this(),m_x,m_y ); return func; } void SetPoint( int x,int y ) { m_x = x; m_y = y; } protected: int m_x; int m_y; };
这样,当我们需要状态保存的时候,不管是Line,Circle,还是Point,调用SaveState即可,如果按照GOF的comment或者memento,我们可能需要定义一堆的LineState或者CircleState和PointState来支持不同的类型保存和恢复,不过付出的代价就是调试困难,毕竟function里面比较难看到里面的数据
看的不是很懂的模式:visitor模式
我也看得不是很懂这个模式,感觉没什么用,如果把那个accept接口换成function,比直接在基类定义一大堆的A,B,C来的好得多,只需要统一提供一个function就可以了