设计模式 (二)——观察者模式(Observer,行为型)
1.概述
使用设计模式可以提高代码的可复用性、可扩充性和可维护性。观察者模式(Observer Pattern)属于行为型模式,在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。
例如,在实际生活中,报纸出版社和订阅者之间存在着一对多的关系。当你向出版社订阅报纸时,只要他们有新报纸出版,就会送一份过来。当你不需要看报纸,可以取消订阅。只要报社还在运营,就会一直有人(或单位)向他们订阅报纸或取消报纸。实际上,出版社+订阅者=观察者模式。观察者模式中,出版社被称为”主题”(Subject),订阅者被称为”观察者”(Observer)。关系如下图所示:
主题与观察者定义了一对多的关系。观察者依赖于此主题,只要主题状态一有变化,观察者就会被通知。
观察者模式结构图:
Subject:抽象主题(抽象被观察者),抽象主题对象把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
ConcreteSubject:具体主题(具体被观察者),将有关状态存入具体观察者对象,在具体主题内部状态发生改变时,给所有注册的观察者发送通知。
Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
ConcrereObserver:具体观察者,是实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。
2.实例
下面给出实际的应用场景,以C++为例,使用观察者模式来解决问题。
假设现在需要完成公司的一个项目,气象站项目。气象站的数据由WeatherData对象提供,包括温度、湿度和气压。项目要求有两种布告板,分别显示目前状况(CurrentStatus)和气象统计(Statistics)。当WeatherData对象获得最新的测量数据时,两种布告板必须实时更新。而且,这是一个可以扩展的气象站,需要公布一组API,好让其他开发人员可以写出自己的布告板。
2.1丑陋的设计
//C++示例代码,运行于VS2015
#include <stdlib.h>
#include <iostream>
using namespace std;
//目前状况类:CurrentStatus
class CurrentStatus {
float temperature=0; //C++11特性,可直接给类成员数据赋默认值
float humidity=0;
float pressure=0;
public:
//显示当前气象信息
void display() {
cout << "CurrentStatus :"<< endl;
cout << "temperature:" << temperature << " humidity:" << humidity<<" pressure:" <<pressure << endl;
}
//更新状态
void update(float temperature,float humidity,float pressure) {
this->temperature= temperature;
this->humidity= humidity;
this->pressure= pressure;
display();//显示当前气象信息
};
};
//气象统计类:Statistics
class Statistics {
float minTemperature=3.4e+38;//最低温度
float maxTemperature=-3.4e+38; //最高温度
public:
//显示气象统计
void display() {
cout << "Statistics :" << endl;
cout << "minTemperature:" << minTemperature << " " << "maxTemperature:" << maxTemperature << endl;
}
//更新状态
void update(float temperature,float humidity,float pressure) {
this->minTemperature = temperature<this->minTemperature? temperature: this->minTemperature;
this->maxTemperature= temperature>this->maxTemperature ? temperature : this->maxTemperature;
display();//显示气象统计
};
};
//WeatherData类
class WeatherData {
public:
float getTemperature(){ return 1; };//仅作示意,简单实现
float getHumidity(){ return 2; };
float getPressure(){ return 3; };
void measurementChanged();
};
void WeatherData::measurementChanged(){
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
CurrentStatus cs;
cs.update(temp, humidity, pressure);
Statistics sta;
sta.update(temp,humidity, pressure);
}
int main() {
WeatherData wd;
wd.measurementChanged();
system("pause");
}
程序执行结果:
CurrentStatus :
temperature:1 humidity:2 pressure:3
Statistics :
minTemperature:1 maxTemperature:1
上面的实现我们能够初步完成气象站所需的功能 ,但是存在以下缺点:在实现函数measurementChanged时,当气象站数据更新时,可以及时的使每一个布告板进行实时更新。但是针对具体实现编程,会导致以后再增加或删除布告板时必须修改measurementChanged。
2.2使用观察者模式
具体实现如下。
抽象观察者(Observer)。里面定义了一个更新的方法:
class Observer {
public:
virtual void update(float temperature,float humidity,float pressure)=0;
};
具体观察者(Concrete Observer)。布告板是观察者,里面实现了更新的方法:
//目前状况布告板:CurrentStatus
class CurrentStatus:public Observer{
...
public:
//具体实现更新接口函数
void update(float temperature,float humidity,float pressure){
//同上
}
//显示当前气象信息
void display(){...} //同上
};
//气象信息统计布告板:Statistics
class Statistics:public Observer{
...
public:
//具体实现更新接口函数
void update(float temperature,float humidity,float pressure{
//同上
}
//显示气象统计信息
void display(){...} //同上
抽象被观察者(Subject)。抽象主题提供了注册attach、移除detach和通知notify三个纯虚函数,供具体被观察者实现:
class Subject{
public:
virtual void attach(Observer*)=0;
virtual void detach(Observer*)=0;
virtual void notify()=0;
};
具体被观察者(Concrete Subject)。这里的WeatherData类是具体主题(具体被观察者),具体实现如下:
class WeatherData:public Subject{
float temperature=0;
float humidity=0;
float pressure=0;
list<Observer*> list; //用于记录注册的观察者
public:
//实现注册
void attach(Observer* o){
list.push_back(o);
}
//实现移除
void detach(Observer* o){
for(auto it=list.begin();it!=list.end();++it){
if(*it==o){
list.erase(it);
break;
}
}
}
//实现通知所有的观察者
void notify(){
for(auto it=list.begin();it!=list.end();++it){
(*it)->update(temperature,humidity,pressure);
}
}
//设置观测值
void setMeasurements(float temperature,float humidity,float pressure){
this->temperature= temperature;
this->humidity= humidity;
this->pressure= pressure;
this->notify();//观测值更新,通知观察者
}
//WeatherData的其他方法
};
气象站已经通过观察者模式完成了建立,下面开始测试。
int main() {
WeatherData wd;
//注册布告板(观察者)
wd.attach(new CurrentStatus);
wd.attach(new Statistics);
//检测到新的气象值,通知布告板进行更新
wd.setMeasurements(1.0,2.0,3.0);
wd.setMeasurements(1.1, 2.1, 3.1);
system("pause");
}
程序运行结果:
CurrentStatus :
temperature:1 humidity:2 pressure:3
Statistics :
minTemperature:1 maxTemperature:1
CurrentStatus :
temperature:1.1 humidity:2.1 pressure:3.1
Statistics :
minTemperature:1 maxTemperature:1.1
我们成功了使用了观察者模式,完成了气象站项目的设计和实现。观察者模式提供了一种对象设计,让主题和观察者之间松耦合,它们之间依然可以交互,但是不太清楚彼此的细节。
任何时候我们都可以增加新的观察者,因为主题唯一依赖的东西是一个实现Observer接口的对象列表。事实上,在运行时我们可以用新的观察者取代现有的观察者,主题不会受到任何影响。同样的,也可以在任何时候删除某些观察者,或者注册某些观察者。此外,当有新类型的观察者出现时,主题的代码不需要修改,只要将新类型的观察者实现Observer接口,然后注册即可。
事实上,我们还可以独立的复用主题或观察者,并且改变主题或观察者中的一方,不会影响另一方,因为二者是松耦合。松耦合的设计可以让我们建立有弹性的OO系统,能够应对变化,因为对象之间的相互依赖降到了最低。所以,我们要坚持一个OO设计原则:为了交互对象之间的松耦合设计而努力。
3.观察者模式的应用场景和优缺点
使用场景:
(1)关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。
(2)事件多级触发场景。跨系统的消息交换场景,如消息队列、事件总线的处理机制。
优点:
解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。
缺点:
在应用观察者模式时需要考虑一下开发效率和运行效率的问题。程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且消息的通知一般是顺序执行,如果一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。
4.小结
(1)OO设计原则:为了交互对象之间的松耦合设计而努力。
(2)观察者模式:在对象之间定义一对多的依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
参考文献
[1]Freeman E.,Freeman E.,Sierra K.,et al.设计模式[M].第一版O’Reilly Taiwan公司译.北京:中国电力出版社,2015:38-75
[2]设计模式(五)观察者模式
设计模式 (二)——观察者模式(Observer,行为型)的更多相关文章
- 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)
原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...
- 8.5 GOF设计模式四: 观察者模式Observer
GOF设计模式四: 观察者模式Observer 现实中遇到的问题 当有许多不同的客户都对同一数据源感兴趣,对相同的数据有不同的处理方式,该如 何解决?5.1 定义: 观察者模式 观察者模式 ...
- 二十四种设计模式:观察者模式(Observer Pattern)
观察者模式(Observer Pattern) 介绍定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新. 示例有一个Message实体类,某些对象 ...
- 设计模式 笔记 观察者模式 Observer
//---------------------------15/04/27---------------------------- //Observer 观察者模式----对象行为型模式 /* 1:意 ...
- 人人都会设计模式:观察者模式--Observer
https://segmentfault.com/a/1190000012295887 观察者模式是抽像通知者和观察者,达到具体通知者跟具体观察者没有偶合.能达到不管是切换通知者,或者是切换观察者,都 ...
- 行为型设计模式之观察者模式(Observer)
结构 意图 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新. 适用性 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面.将这二者封装在独 ...
- 设计模式九: 观察者模式(Observer Pattern)
简介 观察者属于行为型模式的一种, 又叫发布-订阅模式. 如果一个对象的状态发生改变,依赖他的对象都将发生变化, 那么这种情况就适合使用观察者模式. 它包含两个术语,主题(Subject),观察者(O ...
- [设计模式] 19 观察者模式 Observer Pattern
在GOF的<设计模式:可复用面向对象软件的基础>一书中对观察者模式是这样说的:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.当一个 ...
- 设计模式之观察者模式(Observer)(4)
简介 观察者模式(Observer)完美的将观察者和被观察的对象分离开.举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上.面向对象设 ...
随机推荐
- JS 实现计算一段文字中的字节数,字母数,数字数,行数,汉字数。
看到了匹配,第一个想到了用正则表达式,哈哈,果然很方便.不过正则表达式高深莫测!我还没有研究明白啊..目前学了点皮毛.代码如下: <!DOCTYPE html PUBLIC "-//W ...
- M1m2分析报告
个人博客链接: http://www.cnblogs.com/kjzxzzh/p/4074386.html http://www.cnblogs.com/kjzxzzh/p/4027699.html ...
- 《Linux课本》读书笔记 第十七章 模块
设备与模块: 设备类型:块设备(blkdev).字符设备(cdev).网络设备: 模块: 分析hello,world模块代码.Hello_init是模块的入口点,通过module_init()注册到系 ...
- 小学四则运算APP 第二阶段冲刺
第一阶段实现最基本的四则运算计算,最原始的所以还没有美化 xml文件 <LinearLayout xmlns:android="http://schemas.android.c ...
- b总结
Beta 答辩总结 评审表 组名 格式 内容 ppt 演讲 答辩 总计 天机组 15 15 13 15 14 72 PMS 16 16 15 16 16 79 日不落战队 16 17 17 17 17 ...
- [2017BUAA软工]第零次作业
第一部分:结缘计算机 你为什么选择计算机专业?你认为你的条件如何?和这些博主比呢?(必答) 我当初选择计算机,是因为:1.北航的前辈对北航计算机专业评价非常高:2.我也喜欢通过编程来代替我完成 ...
- #Leetcode# 977. Squares of a Sorted Array
https://leetcode.com/problems/squares-of-a-sorted-array/ Given an array of integers A sorted in non- ...
- Ehcache Monitor使用一例
场景介绍:系统集成Shiro,使用Ehcache保存用户登录限制次数,常有用户密码被锁,影响工作效率. 在不考虑集成SSO,LDAP,也不引入身份校验,邮件,短信等解锁特性下.使用Ehcache Mo ...
- JS平滑无缝滚动实现———实现首页广告自动滚动效果(附实例)
本文我们实现纯JS方式的滚动广告效果. 先show一下成品: 首先是网页样式: 1. #demo { 2. background: #FFF; 3. overflow:hidden; 4. borde ...
- Docker镜像加速设置
地址:https://www.daocloud.io/mirror#accelerator-doc 配置 Docker 加速器 Linux MacOS Windows curl -sSL https: ...