设计模式之观察者(OBSERVER)模式
定义
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
Observer模式描述了如何建立这种关系。这一模式中的关键对象是目标(subject)和观察者(observer)。一个目标可以有任意数目的依赖它的观察者。一旦目标的状态发生改变,所有的观察者都得到通知。这种交互也称为发布-订阅(publish-subscribe)。目标是通知的发布者。故观察者模式又名 依赖(Dependents),发布-订阅(Publish-Subscribe)。
适用场景
以下任何一种情况可以使用观察者模式:
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这两者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。
- 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。
代码示例
观察者模式,首先需要定义观察者基类(接口)Observer和目标基类(接口)Subject,观察者基类需要对外提供一个接口函数Update,Update是一个纯虚函数,方便多态的实现,供目标通知观察者更新。
class Subject;
class Observer{
public:
virtual ~Observer();
virtual void Update(Subject* theChangedSubject) = ;
protected:
Observer();
};
目标基类需要提供支持观察者订阅函数Attach;观察者取消订阅函数Detach;对已订阅的所有观察者进行广播的函数Notify,在目标变化后,该函数遍历通知所有已订阅的观察者;以及用于存储已订阅该目标的所有观察者的列表结构List。
class Subject{
public:
virtual ~Subject();
virtual void Attach(Observer *);
virtual void Detach(Observer *);
virtual void Notify();
protected:
Subject();
private:
List<Observer *> *_observers;
};
void Subject::Attach(Observer* o){
_observers->Append(o);
}
void Subject::Detach(Observer *o){
_observers->Remove(o);
}
void Subject::Notify(){
ListIterator<Observer*> i(_observers);
for(i.First(); !i.IsDone(); i.Next()){
i.CurrentItem->Update(this);
}
}
基于目标接口类Subject和观察者接口类Observer,下面是GOF写的设计模式一书中的观察者模式应用——一个简单的时钟,每当定时器滴答一下,定时器(目标)就会通知时钟(观察者)绘制界面更新显示。
ClockTimer是基于目标接口类实现的目标实例,GetHour、GetMinute、GetSecond分别用于获取时、分、秒,Tick函数为计时器实现函数,每过一个滴答时刻,该函数就会调用目标基类的Notify函数通知所有已订阅观察者对象。
class ClockTimer::public Subject{
public:
ClockTimer();
virtual int GetHour();
virtual int GetMinute();
virtual int GetSecond();
void Tick();
};
void ClockTimer::Tick(){
//update internal time-keeping state
//...
Notify();
}
DigitalClock继承Widget和Observer,实现观察者实例。构造函数中初始化目标实现类ClockTimer,并订阅当前观察者对象;析构函数中取消订阅当前观察者对象;Update函数是观察者基类纯虚函数的真正实现,当目标ClockTimer更新,调用Notify时,会调用到当前对象Update函数,在Update函数中再调用Draw更新时钟界面显示。
class DigitalClock::public Widget,public Observer{
public:
DigitalClock(ClockTimer *);
virtual ~DigitalClock();
virtual void Update(Subject *);
//overrides Observer operation
virtual void Draw();
//overrides Widget operation;
//defines how to draw the digital clock
private:
ClockTimer* _subject;
};
DigitalClock::DigitalClock(ClockTimer *s){
_subject = s;
_subject->Attach(this);
}
DigitalClock::~DigitalClock(){
_subject->Detach(this);
}
void DigitalClock::Update(Subject * theChangedSubject){
if(theChangedSubject == _subject){
Draw();
}
}
void DigitalClock::Draw(){
//get the new values from the subject
int hour = _subject->GetHour();
int minute = _subject->GetMinute();
//etc.
//draw the digital clock
}
有了上述观察者和目标的实现类后,就能实现基于观察者设计模式的数字时钟实现,code为:
ClockTimer *timer = new ClockTimer;//新建目标对象
DigitalClock *digitalClock = new DigitalClock(timer);//新建观察者对象并订阅ClockTimer目标对象
上例是目标-观察者一 一对应的示例,比如实现多个相同时间的数字时钟,这时会用到单目标-多观察者模型:
ClockTimer *timer = new ClockTimer;
DigitalClock *digitalClock1 = new DigitalClock(timer);//Observer 1
DigitalClock *digitalClock2 = new DigitalClock(timer);//Observer 2
.
.
.
DigitalClock *digitalClockn = new DigitalClock(timer);//Observer n
当要实现监听多个端口的TCP连接的程序,这时会用到多目标-单观察者模型,需要在上ClockTimer和DigitalClock的基础上稍作修改,将观察者类中的目标用二维指针来存储目标对象,目标类、观察者类以及应用代码如下:
目标类:
class ClientTCPConnect::public Subject{
public:
TcpConnect();
virtual void OnReceivedData();//Received Data
virtual void GetData();
};
void ClientTCPConnect::OnReceivedData(){
//...
Notify();
}
void ClientTCPConnect::GetData(){
//Get TCP Data
}
观察者类:
ServerTCPConnect构造函数中传入目标者类的指针和大小,并订阅所有目标者类。
class ServerTCPConnect::public Observer{
public:
ServerTCPConnect(ClientTCPConnect **, int size);
virtual ~ServerTCPConnect();
virtual void Update(Subject *);
//overrides Observer operation
virtual void DealClientConnect(Subject *);
private:
ClientTCPConnect **_sbuject;
int _size;
};
ServerTCPConnect::ServerTCPConnect(ClientTCPConnect**s, int size){
_subject = s;
for(int i = ; i < _size; i++){
_subject[i]->Attach(this);
}
}
ServerTCPConnect::~ServerTCPConnect(){
for(int i = ; i < _size; i++){
_subject[i]->Detach(this);
}
}
void ServerTCPConnect::Update(Subject * theChangedSubject){
for(int i = ; i < _size; i++){
if(theChangedSubject == _subject[i]){
DealClientConnect(_subject[i]);
}
}
}
void ServerTCPConnect::DealClientConnect(Subject *s){
ClientTCPConnect *tcp = (ClientTCPConnect *)s;
tcp->GetData();//deal with TCP Data
}
应用:
int n=10;
ClientTCPConnect **ClientConnect = new ClientTCPConnect*[n];
for(int i = ; i < n; i++){
ClientConnect[i] = new ClientTCPConnect;
}
ServerTCPConnect *serverConnect = new ServerTCPConnect(ClientConnect, n);
再复杂的就是多目标-多观察者模型,可由上多目标-单观察者和单目标-多观察者模型组合而成。
int n = ;
ClientTCPConnect **ClientConnect = new ClientTCPConnect*[n];
for(int i = ; i < n; i++){
ClientConnect[i] = new ClientTCPConnect;
}
int m = ;
ServerTCPConnect **ServerConnect = new ServerTCPConnect*[m];
for(int j = ; i < m; j++){
ServerConnect[i] = new ServerTCPConnect(ClientConnect, n);
}
上面基本上都是相同的观察者和目标类,那么目标和观察者为不同的类时,该如何组合成多目标——多观察者的实例呢?
目标类ClientTCPConnect,ClientTCPConnect1,、、、,ClientTCPConnect_n的实现:
class ClientTCPConnect::public Subject{
public:
ClientTCPConnect();
virtual void OnReceivedData();//Received Data
virtual void GetData();
};
void ClientTCPConnect::OnReceivedData(){
//...
Notify();
}
void ClientTCPConnect::GetData(){
//Get TCP Data
}
.
.
.
class ClientTCPConnect_n::public Subject{
public:
ClientTCPConnect_n();
virtual void OnReceivedData_n();//Received Data
virtual void GetData_n();
};
void ClientTCPConnect_n::OnReceivedData_n(){
//...
Notify();
}
void ClientTCPConnect_n::GetData_n(){
//Get TCP Data
}
观察者类ServerTCPConnect,ServerTCPConnect1,、、、,ServerTCPConnect_m的实现:
class ServerTCPConnect::public Observer{
public:
ServerTCPConnect(Subject **, int size);
virtual ~ServerTCPConnect();
virtual void Update(Subject *);
//overrides Observer operation
virtual void DealClientConnect(Subject *);
private:
Subject **_subject;
ClientTCPConnect_k *_subject_k;
.
.
.
ClientTCPConnect_l *_subject_l;
int _size;
};
ServerTCPConnect::ServerTCPConnect(Subject **s, int size){
_subject = s;
_subject_k = (ClientTCPConnect_k*)_subject[k];
_subject_k->Attach(this);
.
.
.
_subject_l = (ClientTCPConnect_l*)_subject[l];
_subject_l->Attach(this);
}
ServerTCPConnect::~ServerTCPConnect(){
_subject_k->Detach(this);
.
.
.
_subject_l->Detach(this);
}
void ServerTCPConnect::Update(Subject * theChangedSubject){
for(int i = ; i < _size; i++){
if(theChangedSubject == _subject[i]){
DealClientConnect(_subject[i]);
}
}
}
void ServerTCPConnect::DealClientConnect(Subject *s){
if(ClientTCPConnect *tcp = dynamic_cast<ClientTCPConnect*>(s)){
tcp->GetData();//deal with TCP Data
...
}
.
.
.
else if(ClientTCPConnect_n *tcp = dynamic_cast<ClientTCPConnect_n*>(s)){
tcp->GetData_n();//deal with TCP Data
...
}
}
.
.
.
class ServerTCPConnect_m::public Observer{
public:
ServerTCPConnect_m(Subject **, int size);
virtual ~ServerTCPConnect_m();
virtual void Update(Subject *);
//overrides Observer operation
virtual void DealClientConnect_m(Subject *);
private:
Subject **_subject;
ClientTCPConnect_k *_subject_k;
.
.
.
ClientTCPConnect_l *_subject_l;
int _size;
};
ServerTCPConnect_m::ServerTCPConnect_m(Subject **s, int size){
_subject = s;
_subject_k = (ClientTCPConnect_k*)_subject[k];
_subject_k->Attach(this);
.
.
.
_subject_l = (ClientTCPConnect_l*)_subject[l];
_subject_l->Attach(this);
}
ServerTCPConnect_m::~ServerTCPConnect_m(){
_subject_k->Detach(this);
.
.
.
_subject_l->Detach(this);
}
void ServerTCPConnect_m::Update(Subject * theChangedSubject){
for(int i = ; i < _size; i++){
if(theChangedSubject == _subject[i]){
DealClientConnect_m(_subject[i]);
}
}
}
void ServerTCPConnect_m::DealClientConnect_m(Subject *s){
if(ClientTCPConnect *tcp = dynamic_cast<ClientTCPConnect*>(s)){
tcp->GetData();//deal with TCP Data
...
}
.
.
.
else if(ClientTCPConnect_n *tcp = dynamic_cast<ClientTCPConnect_n*>(s)){
tcp->GetData_n();//deal with TCP Data
...
}
}
观察者模式模型图
说了这么多,有时不如一张图来的直接,观察者模式可以用下UML图来表示,清晰明了!

观察者模式UML图b)
著名的MVC(Model/View/Controller)模式也是基于OBSERVER设计模式的,Model类担任目标的角色,而View是观察者的基类,Controller用于控制应用程序的流程,它处理事件并作出响应。“事件”包括用户的行为和数据 Model 上的改变。

MVC组件之间的典型合作c)
观察者模式的优点是只要订阅/登记了之后,当目标改变时,观察者能自动更新。
参考资料:
a) 《设计模式:可复用面向对象软件的基础》
b) https://en.wikipedia.org/wiki/Observer_pattern
c) https://zh.wikipedia.org/wiki/MVC
设计模式之观察者(OBSERVER)模式的更多相关文章
- Head First 设计模式 —— 02. 观察者 (Observer) 模式
思考题 在我们的一个实现中,下列哪种说法正确?(多选) P42 public class WeatherDate { // 实例变量声明 public void measurementsChanged ...
- Java 实现观察者(Observer)模式
1. Java自带的实现 类图 /** * 观察目标 继承自 java.util.Observable * @author stone * */ public class UpdateObservab ...
- 设计模式C++描述----04.观察者(Observer)模式
一. 概述 Observer 模式要解决的问题为:建立一个一(Subject)对多(Observer)的依赖关系,并且做到当“一”变化的时候,依赖这个“一”的多也能够同步改变. Sbuject 相当于 ...
- Java设计模式之从[星际争霸的兵种升级]分析观察者(Observer)模式
观察者模式定义对象的一种一对多的依赖关系.当一个对象的状态发生改变时.全部依赖于它的对象都会得到通知并被自己主动更新. 一个简单的样例是.在星际争霸的虫族中有一个0基础单位叫做跳狗(Zergling) ...
- 观察者(Observer)模式
观察者模式又叫做发布-订阅模式(Publish.Subscribe)模式.模型-视图模式(Model/View)模式.源-监听器模式(Source/Listener)模式或从属者(Dependents ...
- 面向对象设计模式——观察者(OBSERVER)模式
定义 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新. Observer模式描述了如何建立这种关系.这一模式中的关键对象是目标(subject ...
- 《Head First 设计模式》ch.2 观察者(Observer)模式
观察者模式 定义了对象之间一对多以来,这样一来,当一个对象改变状态时,它所有的依赖者都会收到通知并自动更新 设计原则-松耦合 松耦合将对象之间的互相依赖降到了最低——只要他们之间的接口仍被遵守 观察者 ...
- java观察者(Observer)模式
观察者模式: 试想,在电子商务网站上,一个用户看中了一件一份,但是当时衣服的价格太贵,你需要将衣服收藏,以便等衣服降价时自动通知该用户.这里就是典型的观察模式的例子. 1.观察者模式的 ...
- 设计模式笔记-观察者(Observer)
观察者设计模式应该是比较简单的一个设计模式. 定义 定义了对象之间的一对多依赖,这样以来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新. 简单理解就是: 当1个对象状态有所改变的时候,依 ...
随机推荐
- Supervisor使用说明
Supervisor是一个 Python 开发的 client/server 系统,可以管理和监控类 UNIX 操作系统上面的进程.它可以同时启动,关闭多个进程,使用起来特别的方便. 1.组成部分 s ...
- JDBC批量插入优化addbatch
// 获取要设置的Arp基准的List后,插入Arp基准表中 public boolean insertArpStandardList(List<ArpTable> list) { Con ...
- Hama顶点编程
Hama是基于HDFS上的BSP模型实现. Apache Hame是Google Pregel的开源实现 Pregel是Google提出的一个面向大规模图计算的通用编程模型.许多实际应用中都涉及到大型 ...
- datanode启动不起来的各种原因
一般在数据节点的log日志信息里能找到导致启动不起来的原因. 1.Namenode和Datanode的NamenodeID不一致 描述:一般在集群多次重新格式化HDFS之后,或者刚安装时会碰到.日志信 ...
- linux dns子域授权 split分离解析 缓存dns服务器
DNS子域授权作用:适用于同一个DNS组织父/子域名的解析工作由不同的dns服务器负责父dns服务器应该有为子域名迭代的能力 上下级区域属于不同的机构管理:.cn与.Anonymous.cn.cn需要 ...
- Android WebView的缓存方式分析
WebView的缓存可以分为(1)页面缓存和(2)数据缓存. 页面缓存是指当WebView加载一个网页时的html.JS.CSS等页面或者资源数据.这些缓存资源是由于浏览器的行为而产生,开发者只能通过 ...
- phpcms V9 默认模板文件目录结构_PHPCms教程
PHPCMSV9 默认模板目录结构 templates 框架系统模板目录 announce 公告 show.html 内容页 ...
- Java之indexOf()方法
Java之indexOf()方法 1.方法介绍 (1)indexOf(int ch) 返回指定字符在此字符串中第一次出现处的索引 (2)indexOf(String str) 返回指定子字符串在此字符 ...
- directX枚举系统设备类
void CSysEnumDlg::DisplayFullCategorySet(void){ USES_CONVERSION; HRESULT hr; IEnumMoniker * ...
- C#抽象类应用实例
abstract修饰符可以和类.方法.属性.索引器及事件一起使用,在类声明中使用abstract修饰符以表明这个类只能是其他类的基类. 抽象类的特性 (1)抽象类不能被实例化 (2)抽象类可以包含抽象 ...