Head First 设计模式之观察者模式(Observer Pattern)
前言:
这一节开始学习观察者模式,开始讲之前会先像第一节那样通过一个应用场景来引入该模式。具体场景为:气象站提供了一个WeatherData对象,该对象可以追踪获取天气的温度、气压、湿度信息,WeatherData对象会随即更新三个布告板的显示:目前状况(温度、湿度、气压)、气象统计和天气预报。
1. 基本需求:利用WeatherData对象获取数据、并更新三个布告板:目前状况、气象统计和天气预报
WeatherData类图如下:
说明:
GetTemperature()、GetHumidity()、GetPressure()分别用来获取天气温度、湿度气压,MeasurementsChanged()当气象测量更新此方法会被调用。
分析:
- 可以通过WeatherData的Getter方法获取三个测量值:温度、湿度、气压
- 气象测量更新时会调用MeasurementsChanged()方法
- 需要实现三个天气数据布告板:“目前状况”、“气象统计”、“天气预报”,一旦WeatherData测到新值,这些布告也马上更新
- 此系统可扩展,可以随意的添加或者删除任何布告板。
实现:
public class WeatherData
{
public void MeasurementsChanged()
{
float temp = GetTemperature();
float humidity = GetHumidity();
float pressure = GetPressure();
currentConditionDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
}
//其他方法
}
反思:
我们的这种实现有何不妥?
结合我们第一节中的一些面向对象的原则,这种实现方式会有如下问题:
l 针对具体实现编程,后续新增或者删除布告板必须修改程序
l 未将改变的地方封装起来
这种实现方式与面向对象的一些基本原则是相违背的,目前暂时是实现了用户的需求,但是在后续不具备可扩展行。
2. 引入观察者模式
2.1 出版者+订阅者=观察者模式
出版者:就相当于“主题”(Subject),订阅者相当于“观察者”(Observer)
当出版者(主题)发行新的报纸的时候,所有的观察者(订阅者)就可以收到最新的报纸,同时,当新的观察者(订阅者)加入时,也可以收到最新的报纸,当观察者(订阅者)退订报纸后,就再也收不到新的报纸。
2.2 定义观察者模式
观察者模式定义了对象之间一对多依赖,这样一来,当一个对象状态改变时,它的所有依赖者都会收到通知并自动更新。
主题和观察者定义一对多的关系。观察者依赖于此主题,只要主题状态一有变化,观察者就会被通知,根据通知的风格,观察者可能因此新值而更新。
观察者模式:类图
2.3设计原则:为了交互对象之间的松耦合设计而努力
松耦合的威力
l 当两个对象之间松耦合,它们依然可以交互,但是不清楚彼此的细节。
l 观察者模式提供了一种对象设计,让主题和观察者之间松耦合。
说明:
- 主题只知道观察者实现了某个接口(IObserver接口),不需要知道观察者是谁,或其他细节。
- 任何时候都可以增加或者删除的观察者,主题唯一依赖的是一个实现了IObserver接口的对象列表。
- 新的类型观察者出现时,主题代码不需要修改,只需要在新类型里实现观察者接口,然后注册为观察者即可。
- 可以独立的复用主题或观察者,因为二者松耦合。
- 改变主题或者观察者,并不会影响另一方。因为二者松耦合。
松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的相互依赖讲到了最低。
3. 利用观察者模式设计并实现气象站
3.1 设计气象站
类图:
3.2具体实现
3.2.1主题、观察者、显示接口
/// Description:对象、观察者、显示接口
/// </summary>
public interface ISubject
{
void RegisterObserver(IObserver o);//注册观察者
void RemoveObserver(IObserver o);//删除观察者
void NotifyObervers();//通知观察者
} public interface IObserver
{
void Update(float temp, float humidity, float pressure);
} public interface IDisplayElement
{
void Display();
}
3.2.2 WeatherData类:注册、删除、通知观察者
/// Description:WeatherData 注册、删除、通知观察者
/// </summary>
public class WeatherData:ISubject
{
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure; public WeatherData()
{
observers = new ArrayList();//初始化obervers,用来存储注册的观察者
} /// <summary>
/// 注册观察者
/// </summary>
/// <param name="o"></param>
public void RegisterObserver(IObserver o)
{
observers.Add(o);
} /// <summary>
/// 删除观察者
/// </summary>
/// <param name="o"></param>
public void RemoveObserver(IObserver o)
{
int i = observers.IndexOf(o);
if (i >= )
observers.Remove(o);
} /// <summary>
/// 通知观察者
/// </summary>
public void NotifyObervers()
{
foreach (IObserver o in observers)
{
o.Update(temperature, humidity, pressure);
}
} /// <summary>
/// 当从气象站得到更新观测值时,通知观察者
/// </summary>
public void MeasurementsChanged()
{
NotifyObervers();
} /// <summary>
///
/// </summary>
/// <param name="temperature"></param>
/// <param name="humidity"></param>
/// <param name="pressure"></param>
public void SetMeasurements(float temperature, float humidity, float pressure)
{
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
MeasurementsChanged();
}
}
3.2.3 布告板类,实现了IObserver、IDisplayElement接口
/// Description:创建布告板
/// </summary>
public class CurrentConditionsDisplay:IObserver,IDisplayElement
{
private float temperature;
private float humidity;
private ISubject weatherData; public CurrentConditionsDisplay(ISubject weatherData)
{
this.weatherData = weatherData;
weatherData.RegisterObserver(this);
} public void Update(float temperature, float humidity, float pressure)
{
this.temperature = temperature;
this.humidity = humidity;
Display();
}
public void Display()
{
Console.WriteLine("Current coditions: " + temperature + "F degress and " + humidity + "% humidity");
}
}
3.2.4 测试
WeatherStation.WeatherData weatherData = new WeatherStation.WeatherData();
WeatherStation.CurrentConditionsDisplay currentDisplay = new WeatherStation.CurrentConditionsDisplay(weatherData);
weatherData.SetMeasurements(, , );
结果如下:
4. Java内置的观察者模式
Java内置的观察者模式,许多功能都已经事先准备好了,甚至可以用推(push)或拉(pull)的方式传送数据。
使用java内置观察者模式实现气象站的OO设计类图,如下:
Java内置观察者模式与我们在3小节中明显的差异是WeatherData继承自Observable类,并集成了一些增加、删除、通知观察者的方法。
l 将对象变成观察者
首先还是要实现Observer(观察者)接口,其次调用Observable对象的addObserver()方法即可。
l 可观察者(主题)送出通知
- 先调用setChanged()方法,标记状态已经改变的事实。
- 调用notifyObservers()方法(该方法有2个,任意一个皆可):notifyObservers()或notifyObservers(Object arg)
l 观察者接收通知
观察者实现了Observer的接口,方法签名如下:
update(Observable o,Object arg)
第一个参数为主题本身,让观察者知道是哪个主题通知它的
第二个参数是传入notifyObservers()的数据对象
如果想用“推”的方式将数据给观察者,则可以把数据当做数据对象的方式传给notifyObservers(arg)
如果想用“拉”的方式将数据给观察者,则需要在update()中,通过WeatherData的getTemperature()等方法获取对应的气象值。
缺陷:
Java内置的观察者模式中Observable是一个类,你必须设计一个类去继承它。如果某类相同时具有Observable类和另一个超类的行为,就无法实现,因为java不支持多继承。
同时也限制了Observable的复用能力。
同时,Observable API将setChanged()方法保护了起来,除非继承自Observable类,否则无法创建Observable实例组合到自己的对象中,也违背了面向对象设计的第二个原则:多用组合,少用继承。
5. 总结
l OO原则:
封装变化
多用组合,少用继承
针对接口编程,不针对实现编程
对交互对象之间的松耦合设计而努力(新的OO原则,松耦合的设计更有弹性,更能应对变化)
l OO模式:
观察者模式—在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。
Head First 设计模式之观察者模式(Observer Pattern)的更多相关文章
- 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)
原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...
- 二十四种设计模式:观察者模式(Observer Pattern)
观察者模式(Observer Pattern) 介绍定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新. 示例有一个Message实体类,某些对象 ...
- [设计模式] 19 观察者模式 Observer Pattern
在GOF的<设计模式:可复用面向对象软件的基础>一书中对观察者模式是这样说的:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.当一个 ...
- c#设计模式之观察者模式(Observer Pattern)
场景出发 一个月高风黑的晚上,突然传来了尖锐的猫叫,宁静被彻底打破,狗开始吠了,大人醒了,婴儿哭了,小偷跑了 这个过程,如果用面向对象语言来描述,简单莫过于下: public class Cat { ...
- 设计模式之观察者模式(Observer pattern)
最近参加了一次面试,其中笔试题有一道编程题,在更换掉试题的描述场景后,大意如下: 上课铃声响起,学生A/B/C/D进入教室:下课铃声响起,学生A/B/C/D离开教室. 要求使用设计模式的思想完成铃与学 ...
- 设计模式九: 观察者模式(Observer Pattern)
简介 观察者属于行为型模式的一种, 又叫发布-订阅模式. 如果一个对象的状态发生改变,依赖他的对象都将发生变化, 那么这种情况就适合使用观察者模式. 它包含两个术语,主题(Subject),观察者(O ...
- 【设计模式】观察者模式 Observer Pattern
定义:观察者模式定义了对象之间的一对多依赖.当“主题”(Object)状态改变事,所有依赖它的“观察者”(Observer)都会受到通知并自动更新.主题支持观察者订阅和退订. 观察者模式提供了一种对象 ...
- 【UE4 设计模式】观察者模式 Observer Pattern
概述 描述 定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新.观察者模式又叫做 发布-订阅(Publish/Subscribe)模式 模型-视图(M ...
- 设计模式-观察者模式(Observer Pattern)
观察者模式(Observer Pattern):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己. 观察者 ...
- 设计模式 - 观察者模式(Observer Pattern) 详细说明
观察者模式(Observer Pattern) 详细说明 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...
随机推荐
- Errore Http 404.2
问题一:IIS 7.5 请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理 404.2就一个http status code, 在msdn上说:404.2 - ISAPI or CGI rest ...
- easyui propertygrid 动态绑定
从$.fn.datagrid.defaults继承,覆盖默认值 $.fn.propertygrid.defaults propertygrid 提供用户一个接口,浏览和编辑对象属性,propertyg ...
- mysql批量替换单字段
update 表名 set 字段名 = replace(字段名,'被替换内容','要替换内容'); 指定有人查这个!!!
- MVC SSO登陆 的麻烦事~
前段时间用MVC + Redis 做session搞了个简单的单点登录Web站.真是日了狗的问题多. 今天正好睡不着,做个备忘笔记>_< 实现方法很简单,无非就是从重载个Controlle ...
- 关于python函数的学习记录
1.默认参数必须指向不变对象! 2. extra = {'city': 'Beijing', 'job': 'Engineer'}注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函 ...
- web设计中那些因素可能影响网站后期优化
web设计中那些因素可能影响网站后期优化. 1.网站代码的简洁实用性.网站源文件html代码.js代码.css代码等应尽可能的压缩处理.能用jquery-min.js的最好不要用jquery.js:c ...
- Android RadioButton 语言无法切换问题
1.Dialog在不退出界面的情况下,RadioButton在语言切换时,无法匹配系统语言的问题: 解决办法为:在RadioButton添加属性 android:saveEnabled="f ...
- sql server 中xml 数据类型的insert、update、delete
近日对SQL操作XML作了如下整理: 1.插入 XML DECLARE @myDoc XMLSET @myDoc = '<Root> <ProductDescription Prod ...
- python 读取并显示图片的两种方法
在 python 中除了用 opencv,也可以用 matplotlib 和 PIL 这两个库操作图片.本人偏爱 matpoltlib,因为它的语法更像 matlab. 一.matplotlib 1. ...
- UICollectionViewCell选中高亮状态和UIButton的高亮状态和选中状态
UICollectionViewCell选中高亮状态 //设置点击高亮和非高亮效果! - (BOOL)collectionView:(UICollectionView *)collectionView ...