【GOF设计模式之路】-- Observer

差不多已经有3个月没有更新博客了,不知道都忙什么去了。我一直以来有一个不知道什么时候开始有的准则,要写博文就得写好,在每次的写作中应该总结上次的一些写法,弥补一些不足,同时要看到进步。所以在这3个月期间做了很多次打算要写本篇的,可总是被一些事和当时的心情所影响,为了追求质量,因此跳票到现在了,在此跟大家说声对不起。之前的一个月一篇的承诺没有实现,以后改正。

好了,回到正题,在瞎扯淡就又该说对不起了^_^。本篇打算介绍GOF中行为型模式之观察者模式(Observer),Observer模式应该算是应用和影响都相当广泛的设计模式之一。就拿最近的工作内容(FLASH版MMORPG)来讲,使用的开发语言是Action Script 3,并使用了pureMVC,它属于一种轻量级的MVC(Model/View/Control)框架,Observer在其中拥有举足轻重的地位,MVC实现了业务逻辑层与表示层的解耦。此外,还有很多系统使用到了Observer,游戏开发方面,典型例如Ogre引擎。

观察者模式可以简单的理解成这样几个形式:

发布—订阅(Publish/Subscribe)

模型—视图(Model/View)

源—监听器(Source/Listener)

从属者(Dependents)

从字面上看,我想在你的脑子里已经有了一个初步的结构图。以第一个形式为例,它有发布者和订阅者的区分,就拿本博客来说,如果你订阅了本博客,那么这篇文章发布之后,你作为订阅者,会接收到更新的通知。就发布者的角度,他并不需要关心订阅者是谁,只要你订阅了,我就通知给你。订阅者也不需要随时来本博客看看是否有更新(当然本博随时欢迎您的到来)。这样一来,大家都轻松,也就有了良好的合作与学习的关系。从专业术语上讲,你我之间是一种松耦合关系。

再举一个例子,我们都去过银行办理过银行,现在的银行都提供了专门的排队系统,这个系统至少有两个好处,最大的一个好处是你不用站着排队,也不用怕那些不讲秩序不好好不排队的人。再一个好处就是你不用随时去关注什么时候该轮到你了,因为到你的时候,系统会通过显示屏显示和语音通知你。你完全可以去泡杯茶坐着。

这两个例子,我就不用图示来表示吧,我想你是懂的,脑子形象思考的一个很重要的环节就是在脑子里将晦涩的文字勾勒成一幅画面。这样将抽象的东西转化,你的理解能力和学习能力也就有很大提高了。

好了,还是先写一个简单的例子,以开始介绍的发布和订阅为例,构建一个简单的一对多关系,代码如下:

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. // Observer.h 
  2. #ifndef __OBSERVER_H__ 
  3. #define __OBSERVER_H__ 
  4.  
  5. #include <string> 
  6. #include <list> 
  7.  
  8. class IPublisher; 
  9. class ISubscriber; 
  10.  
  11. typedef std::string Blog; 
  12. typedef std::list< ISubscriber* > SubscriberList; 
  13.  
  14. // -------------------subscriber----------------------- 
  15. class ISubscriber 
  16. public
  17.     virtual ~ISubscriber( void ){}; 
  18.  
  19. public
  20.     virtual void Update( IPublisher* pPub ) = 0; 
  21.  
  22. protected
  23.     ISubscriber( void ){}; 
  24. }; 
  25.  
  26. class Subscriber_Jack : public ISubscriber 
  27. public
  28.     Subscriber_Jack( void ){}; 
  29.     virtual ~Subscriber_Jack( void ){}; 
  30.  
  31. public
  32.     void Update( IPublisher* pPub ); 
  33. }; 
  34.  
  35. class Subscriber_Sam : public ISubscriber 
  36. public
  37.     Subscriber_Sam( void ){}; 
  38.     virtual ~Subscriber_Sam( void ){}; 
  39.  
  40. public
  41.     void Update( IPublisher* pPub ); 
  42. }; 
  43.  
  44.  
  45. // -------------------publisher----------------------- 
  46. class IPublisher 
  47. public
  48.     virtual ~IPublisher( void ){}; 
  49.  
  50. public
  51.     virtual void Subscribe( ISubscriber* pSub ); 
  52.     virtual void Unsubscribe( ISubscriber* pSub ); 
  53.     virtual void Publish( Blog& blog ); 
  54.      
  55.     virtual Blog& GetBlog( void ) = 0; 
  56.  
  57. protected
  58.     IPublisher( void ){}; 
  59.     virtual void SetBlog( Blog& blog ) = 0; 
  60.  
  61. private
  62.     SubscriberList m_subList; 
  63. }; 
  64.  
  65. class Publisher_Mas : public IPublisher 
  66. public
  67.     Publisher_Mas( void ){}; 
  68.     ~Publisher_Mas( void ){}; 
  69.  
  70. public
  71.     Blog& GetBlog( void ); 
  72.  
  73. protected
  74.     void SetBlog( Blog& blog ); 
  75.  
  76. private
  77.     Blog m_Blog; 
  78. }; 
  79.  
  80. #endif 

看这个头文件,定义了两个接口类:IPublisher和ISubscriber,用于表示发布者和订阅者。然后定义了两个实际的订阅者类和一个发布者类:Subscriber_Jack、Subscriber_Sam和Publisher_Mas。Publisher_Mas发布博文,Subscriber_Jack和Subscriber_Sam收到更新的通知。具体实现如下:

  1. // Observer.cpp 
  2. #include <iostream> 
  3. #include "Observer.h" 
  4.  
  5. void Subscriber_Jack::Update( IPublisher* pPub ) 
  6.     std::cout << "Subscriber_Jack: " << pPub->GetBlog() << std::endl; 
  7.  
  8. void Subscriber_Sam::Update( IPublisher* pPub ) 
  9.     std::cout << "Subscriber_Sam: " << pPub->GetBlog() << std::endl; 
  10.  
  11. void IPublisher::Subscribe( ISubscriber* pSub ) 
  12.     m_subList.push_front( pSub ); 
  13.  
  14. void IPublisher::Unsubscribe( ISubscriber* pSub ) 
  15.     if ( pSub != NULL ) 
  16.     { 
  17.         SubscriberList::iterator it = m_subList.begin(); 
  18.         SubscriberList::iterator end = m_subList.end(); 
  19.         while ( it != end ) 
  20.         { 
  21.             if ( ( *it ) == pSub ) 
  22.             { 
  23.                 m_subList.remove( pSub ); 
  24.                 break
  25.             } 
  26.             ++it; 
  27.         } 
  28.     } 
  29.  
  30. void IPublisher::Publish( Blog& blog ) 
  31.     SetBlog( blog ); 
  32.     SubscriberList::iterator it = m_subList.begin(); 
  33.     SubscriberList::iterator end = m_subList.end(); 
  34.     while ( it != end ) 
  35.         ( *it++ )->Update( this ); 
  36.  
  37. void Publisher_Mas::SetBlog( Blog& blog ) 
  38.     m_Blog = blog; 
  39.  
  40. Blog& Publisher_Mas::GetBlog( void
  41.     return m_Blog; 

由此,形成了一个观察和被观察的模式,当需要发布博文的时候,使用Publisher_Mas则可以将博文更新通知发送给每一个订阅者了,代码很简单,直接看如何使用:

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. // main.cpp 
  2. #include "Observer.h" 
  3.  
  4. int main( void
  5.     Publisher_Mas* masefee = new Publisher_Mas(); 
  6.  
  7.     Subscriber_Jack* jack = new Subscriber_Jack(); 
  8.     Subscriber_Sam*  sam  = new Subscriber_Sam(); 
  9.  
  10.     masefee->Subscribe( jack ); 
  11.     masefee->Subscribe( sam ); 
  12.  
  13.     Blog blog = "【GOF设计模式之路】-- Observer"
  14.     masefee->Publish( blog ); 
  15.  
  16.     masefee->Unsubscribe( jack ); 
  17.     masefee->Unsubscribe( sam ); 
  18.  
  19.     delete jack; 
  20.     delete sam; 
  21.     delete masefee; 
  22.  
  23.     return 0; 

输出结果为:

Subscriber_Sam: 【GOF设计模式之路】-- Observer
Subscriber_Jack: 【GOF设计模式之路】-- Observer

本例只是一个简单的实例,健壮度并没有怎么考虑,重在清晰整个模型,加的东西多了,反而可能忽略细节的东西。原理性的东西都尽量使用轻量级的代码来实现。上面的代码,可以用一张简单的图示来表示,如图1:

图1   观察者模式图示

在Update时,向每一个订阅者传递了一个发布者的参数,这个参数是为了订阅者能够同时订阅多个发布者,当然也可以使用其他形式来实现。在实际中,应当灵活处理,设计模式只是提供我们思想,没有严格规定一定的遵循某种形式。

在Ogre中,比较典型的如FrameListener,还有经常也会将键盘和鼠标事件通过Observer来实现,如KeyListener、MouseListener。这种形式与上面的例子大同小异,可以说只换了名字。这里的Lisenter就等于订阅者,继承自上面的抽象Listener,然后可以有一个类似ListenerManager的管理器,用于Dispatch和Register。原理上都差不多,这里就不详细介绍了。

此刻,根据上面介绍的Observer的形式,让我想到了在C语言这样面向过程的语言。C语言没有类,就更没有继承,那么如果我要实现Observer这样的模式该怎么办呢?于是我写了一些代码,参考一下:

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #include <stdio.h> 
  2. #include <string.h> 
  3.  
  4. #pragma warning( disable : 4996 ) 
  5.  
  6. #define MAX_LISTENERS 20 
  7. typedef int ( *Listener )( void* ); 
  8.  
  9. Listener listeners[ MAX_LISTENERS ]; 
  10.  
  11. typedef struct _Publisher 
  12.     char publisher[ 16 ]; 
  13.     char name[ 256 ]; 
  14. }Publisher; 
  15.  
  16. void RegisterListener( Listener listen ) 
  17.     int i; 
  18.     for ( i = 0; i < MAX_LISTENERS; ++i ) 
  19.     { 
  20.         if ( listeners[ i ] == NULL ) 
  21.         { 
  22.             listeners[ i ] = listen; 
  23.             break
  24.         } 
  25.     } 
  26.  
  27. void UnRegisterListener( Listener listen ) 
  28.     int i; 
  29.     for ( i = 0; i < MAX_LISTENERS; ++i ) 
  30.     { 
  31.         if ( listeners[ i ] == listen ) 
  32.         { 
  33.             listeners[ i ] = NULL; 
  34.             break
  35.         } 
  36.     } 
  37.  
  38. void Dispatcher( Publisher* p ) 
  39.     int i; 
  40.     for ( i = 0; i < MAX_LISTENERS; ++i ) 
  41.     { 
  42.         if ( listeners[ i ] != NULL && !listeners[ i ]( p ) ) 
  43.             break
  44.     } 
  45.  
  46. int Listener1( Publisher* p ) 
  47.     printf( "Listener1: %s - %s/n", p->publisher, p->name ); 
  48.     return 1; 
  49.  
  50. int Listener2( Publisher* p ) 
  51.     printf( "Listener2: %s - %s/n", p->publisher, p->name ); 
  52.     return 1; 
  53.  
  54. int main( void
  55.     Publisher blog; 
  56.     strcpy( blog.publisher, "masefee" ); 
  57.     strcpy( blog.name, "【GOF设计模式之路】-- Observer" ); 
  58.  
  59.     RegisterListener( Listener1 ); 
  60.     RegisterListener( Listener2 ); 
  61.  
  62.     Dispatcher( &blog ); 
  63.  
  64.     printf( "/n" ); 
  65.     UnRegisterListener( Listener1 ); 
  66.  
  67.     Dispatcher( &blog ); 
  68.  
  69.     return 0; 

输出结果:

Listener1: masefee - 【GOF设计模式之路】-- Observer
Listener2: masefee - 【GOF设计模式之路】-- Observer

Listener2: masefee - 【GOF设计模式之路】-- Observer

上面是通过注册回调函数来Listen的,有分发器Dispatcher负责分发到每个回调函数。回调函数也有解耦合的作用,调用者完全没有必要了解被调者是谁。上例中,采用的是一个全局数组,也可以实现如list链表等数据结构来存放回调函数。当然这里似乎有点不伦不类,但是在实际中还是经常用到的,还是那句话,设计模式并没有严格的约束,我们完全可以灵活处理,设计模式只是一种方法,一种思想。因此,不论语言本身类型是面向过程类还是面向对象类,都可以用上。况且,面向对象都不能算是面向对象类语言的专利,它也是一种思想。适合任何类型的编程语言。所以本例,C语言中的Observer同样成立。在实际中,可视情况灵活定制设计。

就上面C语言的例子,用一个更加形象的图来表示,可以如下:

图2   Observer之Listener形式

总结:

本篇到此就差不多了,Observer并不难,它主要用于对象间独立的改变和复用,也就是所谓的降低耦合度。还用于某个对象改变时,同时需要改变其它对象,却又不知道具体会有多少个对象会发生改变,这也正体现了松耦合。所以,当你不需要某些对象紧密联系同时又满足前面两条的时候,你就可以考虑使用Observer了。最后再次说明,设计模式只是一种方法和思想,并不是一种约束。在学习和使用时,重在灵活应对,不能像条条款款一样去记忆,那样原本足够发散的脑子,也被你关在了家里。

好了,本文到此为止,由于本人水平有限,以上所有内容仅供参考,同时欢迎拍砖,我只有一个目的,不断提高!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值