设计模式之观察者模式

1、观察者模式又叫做发布-订阅(Publish/Subscribe)模式。

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

UML图如下:


2、观察者模式的特点:
将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维护一致性而使各类紧密耦合,这样会给维护、扩展、和重用都带来不便。

而观察者模式的关键对象是主题Subject和观察者Observer,一个Subject可以有任意数目的依赖它的Observer,一旦Subject的状态改变,所有的Observer都可以得到通知。Subject发出通知时并不需要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。而任何一个具体的观察者不知道,也不需要知道其他观察者的存在。

3、什么时候需要考虑使用观察者模式呢?
答:当一个对象的改变需要同时改变其他对象的时候。而且它不知道具体有多少个对象有待改变时, 应该考虑使用观察者模式,当一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两都封装在独立的对象中使它们各自独立地改变和复用。

总的来说,观察者模式所做的工作其实就是在解除耦合。让耦合的双发都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。

4、观察者模式的不足:
尽管已经用了依赖倒转原则,但是“抽象通知者”还是依赖“抽象观察者”,也就是说,万一没有了抽象观察者这样的接口,通知的功能就完不成了。另外就是每个具体观察者,它不都要调用“更新”方法。

5、C++代码实现
[cpp] view plaincopy
 
  1. #include <iostream>  
  2. #include <vector>  
  3. #include <string>  
  4. #include <algorithm>  
  5. using namespace std;  
  6.   
  7. /*  Observer类,抽象观察者,为所有的具体观察者定义 
  8.     了一个接口,在得到主题的通知时更新自己。这个接口 
  9.     叫做更新接口。抽象观察者一般用一个抽象类或者一个 
  10.     接口实现。更新接口通常包含一个Update()方法,这个 
  11.     方法叫做更新方法。  */  
  12. class Observer  
  13. {  
  14. public:  
  15.     virtual void Update() = 0;  
  16. };  
  17.   
  18. /*  Subject类,可翻译为主题或抽象通知者一般用抽象类或 
  19.     抽象接口实现。它把所有对观察者对象的引用保存在一个 
  20.     聚集里,每个主题都可以有任何数量的观察者。抽象主题 
  21.     提供一个接口,可以增加和删除观察者对象。  */  
  22. class Subject   
  23. {  
  24. private:  
  25.     //观察者对象的引用的聚集容器  
  26.     vector<Observer *> observers;  
  27. public:  
  28.     //添加主题的观察者  
  29.     void Attach(Observer * p_observer) {  
  30.         observers.push_back(p_observer);  
  31.     }  
  32.     //删除主题的观察者  
  33.     void Detach(Observer * p_observer) {  
  34.         vector<Observer *>::iterator it;  
  35.         it = find(observers.begin(), observers.end(), p_observer);  
  36.         observers.erase(it);  
  37.     }  
  38.     //通知所有和主题关联的观察者,主题对象的状态改变了  
  39.     void Notify() {  
  40.         int length = observers.size();  
  41.         for (int i = 0; i < length; ++i) {  
  42.             observers[i]->Update();  
  43.         }  
  44.     }  
  45. };  
  46.   
  47. /*  ConcreteSubject类,具体主题或具体通知者,将有关 
  48.     状态存入具体的观察者对象,在具体主题的内部状态发 
  49.     生改变时,给所有登记过的观察者发出通知。具体主题 
  50.     角色通常用一个具体子类实现 */  
  51. class ConcreteSubject : public Subject  
  52. {  
  53. private:  
  54.     string subjectState;  
  55. public:  
  56.     string GetSubjectState() {  
  57.         return subjectState;  
  58.     }  
  59.     void SetSubjectState(string newState) {  
  60.         subjectState = newState;  
  61.     }  
  62. };  
  63.   
  64. /*  ConcreteObserver类,具体观察者,实现抽象观察者角色 
  65.     所要求的更新接口,一便使本身的状态与主题的状态相协调 
  66.     。具体观察者角色可以保存一个指向具体主题对象的引用。 
  67.     具体观察者角色通常用一个具体子类实现。 */  
  68. class ConcreteObserver : public Observer  
  69. {  
  70. private:  
  71.     ConcreteSubject * p_subject;  
  72.     string name;  
  73.     string observerState;  
  74. public:  
  75.     ConcreteObserver(ConcreteSubject * subject, string name) {  
  76.         ConcreteObserver::p_subject = subject;  
  77.         ConcreteObserver::name = name;  
  78.     }  
  79.   
  80.     void Update() {  
  81.         observerState = p_subject->GetSubjectState();  
  82.         cout << "观察者/"" << name << "/"发现主题对象的新状态是/"" << observerState << "/""<<endl;  
  83.     }  
  84. };  
  85.   
  86. int main()  
  87. {  
  88.     ConcreteSubject s;  
  89.     s.SetSubjectState("上课了");  
  90.     ConcreteObserver * pa = new ConcreteObserver(&s, "X");  
  91.     ConcreteObserver * pb = new ConcreteObserver(&s, "Y");  
  92.     ConcreteObserver * pc = new ConcreteObserver(&s, "Z");  
  93.     s.Attach(pa);  
  94.     s.Attach(pb);  
  95.     s.Attach(pc);  
  96.     s.Notify();  
  97.     cout << endl;  
  98.   
  99.     s.Detach(pa);  
  100.     s.SetSubjectState("上课中");  
  101.     s.Notify();  
  102.     cout << endl;  
  103.   
  104.     s.Detach(pc);  
  105.     s.SetSubjectState("下课了");  
  106.     s.Notify();  
  107.     return 0;  
  108. }  

再举一个实际的例子:

   观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。它还有两个别名,依赖(Dependents),发布-订阅(Publish-Subsrcibe)。可以举个博客订阅的例子,当博主发表新文章的时候,即博主状态发生了改变,那些订阅的读者就会收到通知,然后进行相应的动作,比如去看文章,或者收藏起来。博主与读者之间存在种一对多的依赖关系。下面给出相应的UML图设计。

 可以看到博客类中有一个观察者链表(即订阅者),当博客的状态发生变化时,通过Notify成员函数通知所有的观察者,告诉他们博客的状态更新了。而观察者通过Update成员函数获取博客的状态信息。代码实现不难,下面给出C++的一种实现。

  1. //观察者  
  2. class Observer    
  3. {  
  4. public:  
  5.     Observer() {}  
  6.     virtual ~Observer() {}  
  7.     virtual void Update() {}   
  8. };  
  9. //博客  
  10. class Blog    
  11. {  
  12. public:  
  13.     Blog() {}  
  14.     virtual ~Blog() {}  
  15.     void Attach(Observer *observer) { m_observers.push_back(observer); }     //添加观察者  
  16.     void Remove(Observer *observer) { m_observers.remove(observer); }        //移除观察者  
  17.     void Notify() //通知观察者  
  18.     {  
  19.         list<Observer*>::iterator iter = m_observers.begin();  
  20.         for(; iter != m_observers.end(); iter++)  
  21.             (*iter)->Update();  
  22.     }  
  23.     virtual void SetStatus(string s) { m_status = s; } //设置状态  
  24.     virtual string GetStatus() { return m_status; }    //获得状态  
  25. private:  
  26.     list<Observer* > m_observers; //观察者链表  
  27. protected:  
  28.     string m_status; //状态  
  29. };  

      以上是观察者和博客的基类,定义了通用接口。博客类主要完成观察者的添加、移除、通知操作,设置和获得状态仅仅是一个默认实现。下面给出它们相应的子类实现。     

  1. //具体博客类  
  2. class BlogCSDN : public Blog  
  3. {  
  4. private:  
  5.     string m_name; //博主名称  
  6. public:  
  7.     BlogCSDN(string name): m_name(name) {}  
  8.     ~BlogCSDN() {}  
  9.     void SetStatus(string s) { m_status = "CSDN通知 : " + m_name + s; } //具体设置状态信息  
  10.     string GetStatus() { return m_status; }  
  11. };  
  12. //具体观察者  
  13. class ObserverBlog : public Observer     
  14. {  
  15. private:  
  16.     string m_name;  //观察者名称  
  17.     Blog *m_blog;   //观察的博客,当然以链表形式更好,就可以观察多个博客  
  18. public:   
  19.     ObserverBlog(string name,Blog *blog): m_name(name), m_blog(blog) {}  
  20.     ~ObserverBlog() {}  
  21.     void Update()  //获得更新状态  
  22.     {   
  23.         string status = m_blog->GetStatus();  
  24.         cout<<m_name<<"-------"<<status<<endl;  
  25.     }  
  26. };  
      客户的使用方式:

[cpp]  view plain cop y print ?
  1. //测试案例  
  2. int main()  
  3. {  
  4.     Blog *blog = new BlogCSDN("wuzhekai1985");  
  5.     Observer *observer1 = new ObserverBlog("tutupig", blog);  
  6.     blog->Attach(observer1);  
  7.     blog->SetStatus("发表设计模式C++实现(15)——观察者模式");  
  8.     blog->Notify();  
  9.     delete blog; delete observer1;  
  10.     return 0;  




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值