前言
在网站博客系统中,当我们对某个博主的博客空间比较感兴趣的时候,都会选择订阅该博客,这时候就使我们很方便的获取到博主博客的更新文章了,当我们订阅的博客有更新变化时,它们也自动推送相应的更新通知给所有订阅该博客的订阅者,同时,作为订阅者,可以订阅多个博主博客。像这种一对多的对象之间的依赖关系,一个对象的改变能够影响其他对象的行为,可以通过观察者模式来实现。
观察者模式
观察者用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。在观察者模式中,发生改变的对象称为观察目标 ,而被通知的对象称为观察者 ,一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展
意图 定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象都得到通知并被自动更新。别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。是一种对象行为型模式
参与者
Subject 观察者抽象目标,可以有任意多个观察者观察同一个目标 提供注册和删除观察者对象的接口 同时它定义了通知方法notify()。目标类可以是接口,也可以是抽象类或具体类
ConcreteSubject 具体目标,抽象目标子类,它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知 实现了在目标类中定义的抽象业务逻辑方法
Observer 抽象观察者,对观察目标的改变做出反应,观察者一般定义为接口 为那些在目标发生改变时需获得通知的对象定义一个更新接口update()
ConcreteObserver 具体观察者,维护一个指向ConcreteSubject对象的引用 存储有关状态,这些状态应与目标的状态保持一致 实现Observer的更新接口update()以使自身状态与目标的状态保持一致
当ConcreteSubject发生任何可能导致其观察者与其本身状态不一致的改变时,它将通知它的各个观察者ConcreteObserver 在得到一个具体目标的改变通知后, ConcreteObserver对象可向目标对象查询信息。 ConcreteObserver使用这些信息以使它的状态与目标对象的状态一致
模式结构
代码实现 1.首先定义抽象主题目标类Subject
,并声明Notify()
接口和增删观察者对象的接口:
1 2 3 4 5 6 7 8 9 10 class Subject { public : virtual void Attach (Observer *pObserver ) = 0 ; virtual void Detach (Observer *pObserver ) = 0 ; virtual void Notify () = 0 ; virtual void SetState (int state ) = 0 ; virtual int GetState () = 0 ; };
2.再定义具体的主题目标类ConcreteSubject
,并实现Notify()
及增删观察者接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class ConcreteSubject : public Subject{ private : list <Observer*> m_listObserver; int m_State; public: ConcreteSubject() : m_State(0) {}; public: virtual void Attach(Observer * pObserver ) { m_listObserver.push_back(pObserver ) ; } virtual void Detach(Observer * pObserver ) { m_listObserver.remove(pObserver); } virtual void Notify() { for (list <Observer*>::iterator it = m_listObserver.begin () ; it != m_listObserver.end () ; ++it) { (*it)->Update() ; } } virtual void SetState(int state ) { m_State = state; } virtual int GetState() { return m_State; } };
3.再定义抽象的观察者类Observer
,并声明更新接口Update()
:
1 2 3 4 5 6 7 class Observer { public : virtual void Update () = 0 ; virtual void ShowState () = 0 ; };
4.然后定义具体的观察者类ConcreteObserver
,并实现接口Update()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class ConcreteObserver : public Observer { private : Subject *m_pSubject; int m_State; public : ConcreteObserver(Subject *pSubject): m_pSubject(pSubject),m_State(0 ) {}; public : virtual void Update () { m_State = m_pSubject->GetState(); } virtual void ShowState () { cout << "Concrete Observer State: " << m_State << endl;; } };
5.测试观察者模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void ObserverTest_General() { Subject *pCS = new ConcreteSubject() ; Observer *pCO1 = new ConcreteObserver(pCS ) ; Observer *pCO2 = new ConcreteObserver(pCS ) ; pCS->Attach(pCO1 ) ; pCS->Attach(pCO2 ) ; pCS->SetState(100) ; pCS->Notify() ; pCO1->ShowState() ; pCO2->ShowState() ; pCS->Detach(pCO2 ) ; pCS->SetState(200) ; pCS->Notify() ; pCO1->ShowState() ; pCO2->ShowState() ; SAFE_RELASE_POINTER(pCS ) ; SAFE_RELASE_POINTER(pCO1 ) ; SAFE_RELASE_POINTER(pCO2 ) ; }
6.运行结果:
Concrete Observer State: 100
Concrete Observer State: 100
Concrete Observer State: 200
Concrete Observer State: 100
使用场景
一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使他们可以独立的改变和复用
一个对象的改变将导致一个或多个其他对象也发生改变,而并不知道具体有多少对象将发生改变,也不知道这些对象是谁
优缺点
优点
可以实现表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层充当具体观察者角色
观察目标和观察者之间建立一个抽象的耦合。观察目标只需要维持一个抽象观察者的集合,无须了解其具体观察者。由于观察目标和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次
支持广播通信,观察目标会向所有已注册的观察者对象发送通知,简化了一对多系统设计的难度
满足“开闭原则”的要求,增加新的具体观察者无须修改原有系统代码
缺点
果在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间进行循环调用,可能导致系统崩溃
没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化
MVC架构中也应用了观察者模式,MVC是一种架构模式,它包含三个角色:模型(Model),视图(View)和控制器(Controller)。其中模型可对应于观察者模式中的观察目标,而视图对应于观察者,控制器可充当两者之间的中介者。当模型层的数据发生改变时,视图层将自动改变其显示内容
观察者模式具体实例
博客订阅通知问题 使用观察者模式简单实现前言所述的博客订阅通知功能
代码实现 1.定义抽象的目标主题类Blog
:
1 2 3 4 5 6 7 8 9 10 11 class Blog { public : virtual void AttachBlogObserver (BlogObserver *pBlogObserver ) = 0 ; virtual void DetachBlogObserver (BlogObserver *pBlogObserver ) = 0 ; virtual void Notify () = 0 ; virtual string GetName () = 0 ; virtual void PublishMsg (string msg ) = 0 ; virtual string GetMsg () = 0 ; };
2.定义具体的目标主题类CsdnBlog
,并实现通知接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class CsdnBlog : public Blog{ private : list <BlogObserver*> m_listBlogObserver; string m_BlogName; string m_msg; public: CsdnBlog(string name ) : m_BlogName(name ) , m_msg("" ) {}; public: string GetName() { return m_BlogName; } public: virtual void AttachBlogObserver(BlogObserver * pBlogObserver ) { m_listBlogObserver.push_back(pBlogObserver ) ; cout << "[Blog Observer]" << pBlogObserver->GetName() << " Attached To " << "[Blog]" << m_BlogName << endl; } virtual void DetachBlogObserver(BlogObserver * pBlogObserver ) { m_listBlogObserver.remove(pBlogObserver); cout << "[Blog Observer]" << pBlogObserver->GetName() << " Detached From " << "[Blog]" << m_BlogName << endl; } virtual void Notify() { for (list <BlogObserver*>::iterator it = m_listBlogObserver.begin () ; it != m_listBlogObserver.end () ; ++it) { (*it)->Update(this ) ; } } virtual void PublishMsg(string msg ) { cout << m_BlogName << " Send Message: " << msg << endl; m_msg = msg; } virtual string GetMsg() { return m_msg; } };
3.定义抽象的观察者类BlogObserver
,声明Update()
接口:
1 2 3 4 5 6 7 class BlogObserver { public : virtual void Update (Blog *pBlog ) = 0 ; virtual string GetName () = 0 ; virtual void ShowMsg () = 0 ; };
4.定义具体的观察者类ConcreteBlogObserver
,实现Update()
接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class ConcreteBlogObserver : public BlogObserver{ private : map<Blog*, string > m_mapBlog_Msg; string m_BlogObserverName; public: ConcreteBlogObserver(string name ) : m_BlogObserverName(name ) {}; public: string GetName() { return m_BlogObserverName; } public: virtual void Update(Blog * pBlog ) { cout << "[Blog Observer]" << m_BlogObserverName << " Update Message: " << pBlog->GetMsg() << " From [Blog]" << pBlog->GetName() << endl; map<Blog*, string >::iterator it = m_mapBlog_Msg.begin () ; for (;it != m_mapBlog_Msg.end () ;++it) { if (((*it).first)->GetName() == pBlog->GetName() ) { (*it).second = pBlog->GetMsg() ; return; } } if (it == m_mapBlog_Msg.end () ) { m_mapBlog_Msg.insert(make_pair(pBlog , pBlog ->GetMsg() )); cout << "[Blog Observer]" << m_BlogObserverName << " Add a New " << "[Blog]" << pBlog->GetName() << endl; } } virtual void ShowMsg() { cout << "\nShow All Blog Message:" << endl; for (map<Blog*,string >::iterator it = m_mapBlog_Msg.begin () ; it != m_mapBlog_Msg.end () ; ++it) { cout << "[Blog Observer]" << m_BlogObserverName << " Message: " << (*it).second << " From [Blog]" << ((*it).first)->GetName() << "\n" << endl; } cout << "\n" << endl; } };
5.测试观察者模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 void ObserverTest_Blog() { Blog *pCsdn_Tly = new CsdnBlog("Tly_Bloger" ) ; Blog *pCsdn_Frank = new CsdnBlog("Frank Bloger" ) ; BlogObserver *pBgOb_A = new ConcreteBlogObserver("A" ) ; BlogObserver *pBgOb_B = new ConcreteBlogObserver("B" ) ; BlogObserver *pBgOb_C = new ConcreteBlogObserver("C" ) ; pCsdn_Tly->AttachBlogObserver(pBgOb_A ) ; pCsdn_Tly->AttachBlogObserver(pBgOb_B ) ; pCsdn_Tly->AttachBlogObserver(pBgOb_C ) ; pCsdn_Tly->PublishMsg("I am Tly" ) ; pCsdn_Tly->Notify() ; pBgOb_A->ShowMsg() ; pBgOb_B->ShowMsg() ; pBgOb_C->ShowMsg() ; pCsdn_Frank->AttachBlogObserver(pBgOb_A ) ; pCsdn_Frank->AttachBlogObserver(pBgOb_B ) ; pCsdn_Frank->PublishMsg("I am Frank" ) ; pCsdn_Frank->Notify() ; pBgOb_A->ShowMsg() ; pBgOb_B->ShowMsg() ; pBgOb_C->ShowMsg() ; pCsdn_Tly->DetachBlogObserver(pBgOb_A ) ; pCsdn_Tly->PublishMsg("I am Tly Two" ) ; pCsdn_Tly->Notify() ; pBgOb_A->ShowMsg() ; pBgOb_B->ShowMsg() ; pBgOb_C->ShowMsg() ; pCsdn_Frank->DetachBlogObserver(pBgOb_A ) ; pCsdn_Frank->PublishMsg("I am Frank Two" ) ; pCsdn_Frank->Notify() ; pBgOb_A->ShowMsg() ; pBgOb_B->ShowMsg() ; pBgOb_C->ShowMsg() ; SAFE_RELASE_POINTER(pBgOb_A ) ; SAFE_RELASE_POINTER(pBgOb_B ) ; SAFE_RELASE_POINTER(pBgOb_C ) ; SAFE_RELASE_POINTER(pCsdn_Tly ) ; SAFE_RELASE_POINTER(pCsdn_Frank ) ; }
6.运行结果:
[Blog Observer]A Attached To [Blog]Tly_Bloger
[Blog Observer]B Attached To [Blog]Tly_Bloger
[Blog Observer]C Attached To [Blog]Tly_Bloger
Tly_Bloger Send Message: I am Tly
[Blog Observer]A Update Message: I am Tly From [Blog]Tly_Bloger
[Blog Observer]A Add a New [Blog]Tly_Bloger
[Blog Observer]B Update Message: I am Tly From [Blog]Tly_Bloger
[Blog Observer]B Add a New [Blog]Tly_Bloger
[Blog Observer]C Update Message: I am Tly From [Blog]Tly_Bloger
[Blog Observer]C Add a New [Blog]Tly_Bloger
Show All Blog Message:
[Blog Observer]A Message: I am Tly From [Blog]Tly_Bloger
Show All Blog Message:
[Blog Observer]B Message: I am Tly From [Blog]Tly_Bloger
Show All Blog Message:
[Blog Observer]C Message: I am Tly From [Blog]Tly_Bloger
[Blog Observer]A Attached To [Blog]Frank Bloger
[Blog Observer]B Attached To [Blog]Frank Bloger
Frank Bloger Send Message: I am Frank
[Blog Observer]A Update Message: I am Frank From [Blog]Frank Bloger
[Blog Observer]A Add a New [Blog]Frank Bloger
[Blog Observer]B Update Message: I am Frank From [Blog]Frank Bloger
[Blog Observer]B Add a New [Blog]Frank Bloger
Show All Blog Message:
[Blog Observer]A Message: I am Tly From [Blog]Tly_Bloger
[Blog Observer]A Message: I am Frank From [Blog]Frank Bloger
Show All Blog Message:
[Blog Observer]B Message: I am Tly From [Blog]Tly_Bloger
[Blog Observer]B Message: I am Frank From [Blog]Frank Bloger
Show All Blog Message:
[Blog Observer]C Message: I am Tly From [Blog]Tly_Bloger
[Blog Observer]A Detached From [Blog]Tly_Bloger
Tly_Bloger Send Message: I am Tly Two
[Blog Observer]B Update Message: I am Tly Two From [Blog]Tly_Bloger
[Blog Observer]C Update Message: I am Tly Two From [Blog]Tly_Bloger
Show All Blog Message:
[Blog Observer]A Message: I am Tly From [Blog]Tly_Bloger
[Blog Observer]A Message: I am Frank From [Blog]Frank Bloger
Show All Blog Message:
[Blog Observer]B Message: I am Tly Two From [Blog]Tly_Bloger
[Blog Observer]B Message: I am Frank From [Blog]Frank Bloger
Show All Blog Message:
[Blog Observer]C Message: I am Tly Two From [Blog]Tly_Bloger
[Blog Observer]A Detached From [Blog]Frank Bloger
Frank Bloger Send Message: I am Frank Two
[Blog Observer]B Update Message: I am Frank Two From [Blog]Frank Bloger
Show All Blog Message:
[Blog Observer]A Message: I am Tly From [Blog]Tly_Bloger
[Blog Observer]A Message: I am Frank From [Blog]Frank Bloger
Show All Blog Message:
[Blog Observer]B Message: I am Tly Two From [Blog]Tly_Bloger
[Blog Observer]B Message: I am Frank Two From [Blog]Frank Bloger
Show All Blog Message:
[Blog Observer]C Message: I am Tly Two From [Blog]Tly_Bloger