分享
 
 
 

大卫的Design Patterns学习笔记19:Observer

王朝other·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

一、概述

Observer(观察者)模式又被称作发布-订阅(Publish-Subscribe)模式,用于定义对象间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

二、结构

Observer模式的结构如下图所示:

图1、Observer模式类图示意

上面的类图中包括如下组成部分:

Subject(抽象主题)角色:主题角色把所有对观察考对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,主题角色又叫做抽象被观察者(Observable)角色,一般用一个抽象类或者一个接口实现。

Observer(抽象观察者)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者角色一般用一个抽象类或者一个接口实现。在这个示意性的实现中,更新接口只包含一个方法(即Update()方法),这个方法叫做更新方法。

ConcreteSubject(具体主题)角色:将有关状态存入具体现察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者角色(Concrete Observable)。具体主题角色通常用一个具体子类实现。

ConcreteObserver(具体观察者)角色:存储与主题的状态自恰的状态。具体现察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体现察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。

三、应用

在以下任一情况下可以使用观察者模式:

1、当一个抽象模型有两个方面,其中一个方面依赖于另一方面,将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

2、当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。

3、当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。

四、优缺点

Observer模式实现了表示层和数据逻辑层的分离,允许你独立的改变目标和观察者。你可以单独复用目标对象而无需同时复用其观察者, 反之亦然。它也使你可以在不改动目标和其他的观察者的前提下增加观察者。

下面是观察者模式其它一些优缺点:

1、目标和观察者间的抽象耦合一个目标所知道的仅仅是它有一系列观察者,每个都符合抽象的Observer类的简单接口。目标不知道任何一个观察者属于哪一个具体的类。这样目标和观察者之间的耦合是抽象的和最小的。

因为目标和观察者不是紧密耦合的,它们可以属于一个系统中的不同抽象层次。一个处于较低层次的目标对象可与一个处于较高层次的观察者通信并通知它,这样就保持了系统层次的完整。如果目标和观察者混在一块,那么得到的对象要么横贯两个层次(违反了层次性),要么必须放在这两层的某一层中(这可能会损害层次抽象)。

2、支持广播通信不像通常的请求,目标发送的通知不需指定它的接收者。通知被自动广播给所有已向该目标对象登记的有关对象。目标对象并不关心到底有多少对象对自己感兴趣;

它唯一的责任就是通知它的各观察者。这给了你在任何时刻增加和删除观察者的自由。处理还是忽略一个通知取决于观察者。

3、意外的更新因为一个观察者并不知道其它观察者的存在,它可能对改变目标的最终代价一无所知。在目标上一个看似无害的的操作可能会引起一系列对观察者以及依赖于这些观察者的那些对象的更新。此外,如果依赖准则的定义或维护不当,常常会引起错误的更新,这种错误通常很难捕捉。

简单的更新协议不提供具体细节说明目标中什么被改变了,这就使得上述问题更加严重。如果没有其他协议帮助观察者发现什么发生了改变,它们可能会被迫尽力减少改变。

五、举例

Observer模式是一个应用比较广泛的模式,在Java中,Observer模式的应用随处可见(可参考笔记13中关于Java中CoR与Observer模式的对比部分内容),此外,JDK中特别提供了java.util.Observable类和Observer接口来方便程序员应用Observer模式,而C++方面,ATL则提供了IConnectionPointContainer、IConnectionPoint、IEnumConnectionPoints、IEnumConnections等以支持所谓的连接点及可连接对象等(相关示例见参考1),而在MFC中,一个文档可以对应多个视图,而当文档发生更新后,可以通过UpdateAllViews来同步更新所有视图,这虽然并非严格意义上的Observer模式的应用,但其本质是相似的。

前面曾经从Mediator模式的角度实现过一个ChatRoom的例子,这里用Observer模式来解决同一问题:

#include <vector>

#include <iostream>

using namespace std;

class ChatRoom;

struct Observer

{

virtual void SetChatRoom(ChatRoom* pChatRoom) = 0;

virtual void Notify(const string& from, const string& to, const string& msg) = 0;

};

class ChatRoom // Subject

{

private:

vector<Observer*> vo;

public:

virtual ~ChatRoom()

{

vector<Observer*>::iterator vi = vo.begin();

for (; vi != vo.end(); vi++)

{

delete *vi;

}

}

void Login(Observer* po)

{

vo.push_back(po);

po->SetChatRoom(this);

}

void Logout(Observer* po)

{

vector<Observer*>::iterator vi = vo.begin();

for (; vi != vo.end(); vi++)

{

if (*vi == po)

vo.erase(vi);

}

}

void Notify(const string& from, const string& to, const string& msg)

{

vector<Observer*>::iterator vi = vo.begin();

for (; vi != vo.end(); vi++)

{

(*vi)->Notify(from, to, msg);

}

}

};

class Chater : public Observer // ConcreteObserver

{

private:

string name;

ChatRoom* pChatRoom;

public:

Chater(const string& name) : name(name) {}

void SetChatRoom(ChatRoom* pChatRoom)

{

this->pChatRoom = pChatRoom;

}

void Send(const string& to, const string& msg)

{

pChatRoom->Notify(name, to, msg);

}

void Notify(const string& from, const string& to, const string& msg)

{

if (0 == to.compare(name))

{

cout << "[" << from.c_str() << "] to [" << to.c_str() << "]: "

<< msg.c_str() << endl;

}

}

};

int main()

{

ChatRoom cr;

Chater* c1 = new Chater("David");

Chater* c2 = new Chater("Jordan");

Chater* c3 = new Chater("O'Neal");

cr.Login(c1);

cr.Login(c2);

cr.Login(c3);

c1->Send("Jordan", "You are a great basketball player"); // Send message to change subject state

c2->Send("O'Neal", "Work hard, you are great!");

return 0;

}

在上述示例中,引起Subject(即ChatRoom)发生变化的是Observer(即Chater),而接收Subject发出的Notify消息的同样是Observer,这在有些应用中可能是两个/类不同的实体。此外,在上述示例中,当ChatRoom接收到消息时,不会对消息的内容进行解析,而是直接通过Notify通知所有Chater,由所有Chater自己负责检查该消息是否是发送给自己的(实际的ChatRoom不可能采用这种形式),这在一定程度上与广播非常相似,所以有时候,我们可以采用Observer模式来模拟软件广播。

以上示例中,Subject不对消息的内容进行检查,而是盲目地进行广播,在具体应用中,我们可以结合使用Mediator模式和Observer模式,将Subject设计成一个Mediator,对消息内容进行检查,进而根据消息内容进行消息的Publish。

参考:

1、http://www.codeproject.com/com/connectionpoint.asp

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有