观察者模式(行为型)

前言


在网站博客系统中,当我们对某个博主的博客空间比较感兴趣的时候,都会选择订阅该博客,这时候就使我们很方便的获取到博主博客的更新文章了,当我们订阅的博客有更新变化时,它们也自动推送相应的更新通知给所有订阅该博客的订阅者,同时,作为订阅者,可以订阅多个博主博客。像这种一对多的对象之间的依赖关系,一个对象的改变能够影响其他对象的行为,可以通过观察者模式来实现。

观察者模式


观察者用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。在观察者模式中,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展

意图

定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象都得到通知并被自动更新。别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。是一种对象行为型模式

参与者

  • Subject
    观察者抽象目标,可以有任意多个观察者观察同一个目标
    提供注册和删除观察者对象的接口
    同时它定义了通知方法notify()。目标类可以是接口,也可以是抽象类或具体类

  • ConcreteSubject
    具体目标,抽象目标子类,它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知
    实现了在目标类中定义的抽象业务逻辑方法

  • Observer
    抽象观察者,对观察目标的改变做出反应,观察者一般定义为接口
    为那些在目标发生改变时需获得通知的对象定义一个更新接口update()

  • ConcreteObserver
    具体观察者,维护一个指向ConcreteSubject对象的引用
    存储有关状态,这些状态应与目标的状态保持一致
    实现Observer的更新接口update()以使自身状态与目标的状态保持一致

当ConcreteSubject发生任何可能导致其观察者与其本身状态不一致的改变时,它将通知它的各个观察者ConcreteObserver
在得到一个具体目标的改变通知后, ConcreteObserver对象可向目标对象查询信息。
ConcreteObserver使用这些信息以使它的状态与目标对象的状态一致

模式结构

observer

代码实现

1.首先定义抽象主题目标类Subject,并声明Notify()接口和增删观察者对象的接口:

1
2
3
4
5
6
7
8
9
10
// Abstract Subject
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
// ConcreteSubject
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(); // 调用各个观察者的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
// Abstract Observer
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
// ConcreteObserver
class ConcreteObserver : public Observer
{
private:
Subject *m_pSubject; // 具体的观察者维护一个具体的主题目标对象引用
int m_State; // 观察者状态
public:
ConcreteObserver(Subject *pSubject): m_pSubject(pSubject),m_State(0) {};// 具体的Observer关联的Subject,调用该Subject的方法Update()
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(); // 100
pCO2->ShowState(); // 100

pCS->Detach(pCO2);

pCS->SetState(200);
pCS->Notify();

pCO1->ShowState(); // 200
pCO2->ShowState(); // 100

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
// Abstract Blog
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
// Concrete Blog: Csdn Blog
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); // Update本Blog对象的Message到所有订阅该Blog对象的Observer
}
}
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; // Update指定Blog对象的Message
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
// Concrete Blog Observer
class ConcreteBlogObserver : public BlogObserver
{
private:
map<Blog*, string> m_mapBlog_Msg; // 保存订阅的Blog及对应的消息
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(); // 找到了直接替换跟新Msg
return;
}
}
// 未找到,则插入到map
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) // 显示所有list中的Message
{
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);

// 第一个Blog:Tly Blog发布并通知跟新消息到A,B,C Observer
pCsdn_Tly->PublishMsg("I am Tly");
pCsdn_Tly->Notify();

// 依次显示A,B,C Observer订阅的所有Blog(此时均只有Tly)的信息
pBgOb_A->ShowMsg();
pBgOb_B->ShowMsg();
pBgOb_C->ShowMsg();

pCsdn_Frank->AttachBlogObserver(pBgOb_A);
pCsdn_Frank->AttachBlogObserver(pBgOb_B);

// 另外一个Blog:Frank Blog发布并通知跟新消息到A,B Observer
pCsdn_Frank->PublishMsg("I am Frank");
pCsdn_Frank->Notify();

// 依次显示A,B,C Observer订阅的所有Blog(此时有A,B有Tly、Frank,C只有Tly)的信息
pBgOb_A->ShowMsg();
pBgOb_B->ShowMsg();
pBgOb_C->ShowMsg();

// A 取消订阅了Tly, Tly 重新发布消息到B,C Observer
pCsdn_Tly->DetachBlogObserver(pBgOb_A);
pCsdn_Tly->PublishMsg("I am Tly Two");
pCsdn_Tly->Notify();

// 依次显示A,B,C Observer订阅的所有Blog的信息
pBgOb_A->ShowMsg();
pBgOb_B->ShowMsg();
pBgOb_C->ShowMsg();

// A 取消订阅了Frank,Frank 重新发布消息到B Observer
pCsdn_Frank->DetachBlogObserver(pBgOb_A);
pCsdn_Frank->PublishMsg("I am Frank Two");
pCsdn_Frank->Notify();

// 依次显示A,B,C Observer订阅的所有Blog的信息
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