学习笔记 ---- 设计模式之观察者模式
转载: http://www.cnblogs.com/shitouer/archive/2011/09/02/2164048.html
Head First里边给出的场景是:
客户需要这样一个App:一个WheatherData类可以从气象站获得气象信息(temperature, humidity, pressure and so on).一旦这些信息改变,就会更新气象站的三个公告板(CurrentConditionBulletin, StatisticsBulletin and ForecastBulletin, 另外,用户还可以随时增加/删除/更改自己的布告板)。
客户给出了WheatherData的类图:
结合以上描述和类图我们已知:
1. 由Getter可以或得气象信息
2. 一旦气象信息有改变,调用measurementsChanged()方法更新所有的布告板
3. 现在需要三个布告板
4. Customer可以定义自己的布告板
首先看一个不好的实现:
publicclass WeatherData { publicvoid MeasurementsChanged(){floattemp = getTemperature(); floathumidity = getHumidity(); floatpressure = getPressure();currentConditionsDisplay.update(temp, humidity, pressure); statisticsDisplay.update(temp, humidity, pressure);forecastDisplay.update(temp, humidity, pressure);}
}
为什么不好呢?可以从已经知道的几个设计原则入手:
1. 找出程序中变化的部分,将其与固定不变的部分隔离
在这个场景中,变化的是布告栏的类型和数量,在这个不好的例子中,我们改变布告栏的类型和数量,势必要修改WheatherData这个类。如何才能可以随意修改布告栏的数量和类型,却不用修改WheatherData类呢?
2. 针对接口编程,不针对实现编程
明显,statisticsDisplay,statisticsDisplay,forecastDisplay均是具体的实现的编程,如果以后我们增加修改布告栏,都必须修改这段程序。而且三个方法( update() )的名字和参数相同,一个统一的接口可行?
可以根据报纸订阅业务对观察者模式有一个简单的了解:
观察者模式类图:
Subject
主题接口,也即可观察者(Observable),对象使用此接口注册为观察者,或者把自己从观察着中删除。每个主题可以有多个观察者。
ConcreteSubject
一个具体主题实现了主题接口,除了注册和撤销之外,具体主题还实现了notifyObservers()方法,这个方法用来在主题状态改变时更行所有的观察者。具体主题也可能有设置和获取状态的方法。
Observer
所有潜在的观察者必须实现观察者接口,这个接口只有update()方法,当主题改变时,它被调用。
ConcreteObserver
具体的观察者可以是任何实现了Observer接口的类。观察者必须注册具体主题,一边接收更新。
基本原则:为了交互对象之间的松耦合设计而努力
观察者模式的定义:观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有的依赖者都会收到通知并自动更新。
观察者模式中,一对多的关系体现在哪里?
这个模式中,主题是具有状态的对象,并且可以控制这个状态,也就是说,是一个具有“状态”的主题。
另一方面,观察者使用这些状态,虽然这些状态并不属于他们。有多个观察者,依靠主题来告诉他们主题状态何时变了。
这就产生了一个关系,一个“主题”对多个“观察者”的关系
松耦合设计威力体现在哪里?
当两个对象松耦合,他们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计,让主题和观察者松耦合。
下面我们根据观察者模式来画出开篇WheatherStation的类图:
代码如下
ISubject:
?
importorg.jpatterns.gof.ObserverPattern;@ObserverPattern(comment="ISubject: Interface of Observer Pattern"
)
publicinterface ISubject {voidregisterObserver(IObserver o);voiddeleteObserver(IObserver o);voidnotifyObservers();
}IObserver:
?
importorg.jpatterns.gof.ObserverPattern;@ObserverPattern(comment="IObserver: Interface of Observer Pattern"
)
publicinterface IObserver {voidupdate(floattemp, floathumidity, floatpressure);
}实现了ISubject接口的WheatherData:
?
importjava.util.ArrayList;importorg.jpatterns.gof.ObserverPattern;@ObserverPattern(comment = "WheatherData: Concrete Subject of Observer Pattern")
publicclass WheatherData implementsISubject {privateArrayList<IObserver> observers;privatefloat temperature;privatefloat humidity;privatefloat pressure;publicWheatherData() {this.observers = newArrayList<IObserver>();}@Overridepublicvoid registerObserver(IObserver o) {observers.add(o);}@Overridepublicvoid deleteObserver(IObserver o) {if(observers.indexOf(o) >= 0) {observers.remove(o);}}@Overridepublicvoid notifyObservers() {for(inti = 0; i < observers.size(); i++) {IObserver observer = observers.get(i);observer.update(temperature, humidity, pressure);}}publicvoid measurementsChanged() {this.notifyObservers();}publicvoid setMeasurements(floattemperature, floathumidity, floatpressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;this.measurementsChanged();}
}实现了IObserver接口的一个Bulletin:
?
publicclass CurrentConditionsBulletin implementsIObserver, IDisplayElement {privatefloat temperature;privatefloat humidity;privateISubject wheatherData;publicCurrentConditionsBulletin(ISubject wheatherData) {this.wheatherData = wheatherData;wheatherData.registerObserver(this);}@Overridepublicvoid update(floattemp, floathumidity, floatpressure) {this.temperature = temp;this.humidity = humidity;this.display();}@Overridepublicvoid display() {System.out.println("Current Conditions: " + this.temperature+"F Degrees and " + this.humidity + "% Humidity");}
}创建气象站,将程序run起来:
?
publicclass WheatherStation {publicstatic void main(String[] args) {WheatherData wheatherData = newWheatherData();IObserver o1 = newCurrentConditionsBulletin(wheatherData);IObserver o2 = newStatisticsBulletin(wheatherData);IObserver o3 = newForecastBulletin(wheatherData);IObserver o4 = newCustomBulletin(wheatherData);//自定义的wheatherData2.setMeasurements(20,20,20);}}
省略了好多注释啊,想好好理解,还是推荐去看HeadFirst吧,说实话,真本书真好!
其实在java中已经有内置的观察者模式。java.util中的Observable(类)和Observer(接口)。但是缺点不少,例如违反了:针对接口变成,而不是针对实现编程和多用组合少用继承两个原则,而且起nofify也依赖顺序。
观察者模式的要点:
观察者定义了对象之间一对多的关系。
主题用一个共同的接口来更新观察者。
观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。
使用此模式,可以从可观察者推或者拉数据。
有多个观察者时,不可以依赖特定的通知次序。
要注意java.util.Observable实现上带来的问题。
如果有必要的话,可以实现自己的Observable。
观察者模式的应用
观察者模式的优点
观察者和可观察者之间是抽象耦合
建立了一套完整的触发链
观察者模式的缺点
多级触发的效率问题
Java中消息的通知默认是顺序执行的,一个观察者卡壳,会影响整体的效率
观察者模式的使用场景
可拆分的关联行为场景
事件多级触发的场景
跨系统的消息交换场景
学习笔记 ---- 设计模式之观察者模式相关推荐
- Unity游戏框架学习笔记——03基于观察者模式的事件中心
Unity游戏框架学习笔记--03基于观察者模式的事件中心 基于观察者模式的事件中心 一如既往指路牌:https://www.bilibili.com/video/BV1C441117wU?p=5. ...
- C++ 设计模式(可复用面向对象软件的基础)学习笔记——第五讲 观察者模式
对于设计模式初学者来说,GoF的这本设计模式(可复用面向对象软件的基础)是有些难理解的,因此我先看了视频教程,附上教程链接网址https://www.bilibili.com/video/av5225 ...
- [学习笔记]设计模式之Command
为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在上篇Chain of Responsibility(职责链)模式笔记中,我们学习了一种行为型设计模式.今天,我们继续这一主题,来学习 ...
- [学习笔记]设计模式之Decorator
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 Decorator(装饰)模式,可以动态地给一个对象添加一些额外的职能.为了更好地理解这个模式,我们将时间线拉回Bridge模式笔记的 ...
- 【HeadFirst 设计模式学习笔记】2 观察者模式
1.我们需要理解报社.订阅系统和订报人之间的关系,订报人通过订阅系统订报,一旦报社有新的报纸,订阅系统就会派人送或者邮寄给订报人新的报纸.然后,出版者+订阅者就是观察者模式,只不过名称不一样,主题(S ...
- [学习笔记]设计模式之Singleton
写在前面 为方便读者,本文已添加至索引: 设计模式 魔法手札索引 在前几篇笔记中,我们有了解了部分对象创建型模式,包括Builder(建造者).Abstract Factory(抽象工厂)和Facto ...
- [学习笔记]设计模式[6]-{适配器模式外观模式}
设计原则 最少知识原则:只和你的密友谈话 这个原则的意思是,在系统设计的过程中,不要让太多的类耦合在一起,免得对系统一部分的修改会影响到其他部分.在设计系统之前,应该首先注意对象与对象之间的交互关系, ...
- sheng的学习笔记-设计模式-代理模式
原理图: 代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问.这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介. 至少在以下集中情况下可以用 ...
- sheng的学习笔记-设计模式-单例模式
转载文章:Java单例模式的5种实现方法_明朗晨光的专栏-CSDN博客_单例模式java 饿汉 类加载的时候就创建了实例 优点:类加载的时候创建一次实例,避免了多线程同步问题 ...
最新文章
- 下拉菜单连动效果的一种用法
- jasmine.any(Function)
- oracle spring 分页查询,SpringJDBC 调用oracle 通用存储过程分页
- 静态变量加载时间,静态代码块加载时间
- 华为Mate 20 X(5G)评测:6199元的5G双模旗舰手机
- 单片机ADC采样算法----中位值平均滤波法
- 微软推出 “Edge 漏洞研究计划”,类似于谷歌的 Project Zero 项目
- 百度地图实现定位图标随手机方向变化而变化,即运用方向传感器
- 计算机重启 ie 被改,ie被修改怎么办 ie被修改的解决方法【详解】
- Netcdf4.4的安装过程(附netcdf4.1.3的安装过程)
- sapi 实现语音朗读
- 数字和ASII码之间的转换
- 常见嵌入式WEB服务器
- 【PHP】单词十以内的加法de多种解法
- python写一个飞花令程序
- linux系统旋转屏幕命令,Archlinux 实现屏幕旋转
- mosquitto 在 Windows 上的安装
- hihocoder1081
- Unity减少安装包的大小总结
- Codeforces Round #533(Div. 2) A.Salem and Sticks
热门文章
- Excel导入导出组件的设计
- TP/TCP/UDP
- 同步异步、阻塞非阻塞
- 推荐游戏玩家使用金山密保保护帐号安全
- 基于vue-cli3+typescript+element-ui搭建起来的后端管理平台框架(骨架)
- 全球最大电子展2018再度起航,镁客网在香港带你深度探讨人工智能
- python基本数据类型(一)-python3.0学习笔记
- java程序内存泄漏场景及预防
- (七)Oracle学习笔记—— 游标
- 《iOS应用开发指南——使用HTML5、CSS3和JavaScript》——第1章 变小的巨大影响1.1 移动魔力和掌上电脑...