《Head First 设计模式》之观察者模式——天气显示
观察者模式(Observer)
——在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。
(出版者Subject+订阅者Observer=观察者模式)
- 特点:定义并维护对象之间的一对多关系
- 原则:为交互对象之间的松耦合设计而努力
- 示例(气象站类图)

三个接口:
public interface Subject{
public void registerObserver(Observer o);//注册观察者
public void removeOberver(Observer o);//删除观察者
public void notifyObserver();//通知观察者
}
public interface Observer{
public void update(float temp, float humidity, float pressure);
}
public interface DisplayElement{
public void display();
}
在WeatherData中实现主题接口:
public class WeatherData implements Subject{
private ArrayList observers;//记录观察者
private float temperature;
private float humidity;
private float pressure;
public WeatherData(){
observers = new ArrayList();
}
public void registerObserver(Observer o){//注册观察者
observers.add(o);
}
public void removeOberver(Observer o){//删除观察者
int i = observers.indexOf(o);
if(i >= 0){
observers.remove(0);
}
}
public void notifyObservers(){
for(int i = 0; i < observers.size(); i++){ //通知每一个观察者
Observer observer = (Observer)observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
public void measurementsChanged(){
notifyObservers();
}
public void setMeasurements(float temp, float hum, float pre){
this.temperature = temp;
this.humidity = hum;
this.pressure = pre;
measurementsChanged();
}
}
实现CurrentConditionDisplay.java
public class CurrentConditionsDisplay implements Observer, DisplayElement{
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData){//构造器需要weatherData对象作为注册之用
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temp, float humidity, float pressure){
this.temperature = temp;
this.humidity = humidity;
display();
}
public void display(){
//输出
}
}
测试程序:
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();//建立weatherData对象
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);//将对象传给三个Observer,即add观察者
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}
Java内置的观察者模式
上述示例实现信息由Subject “推送(push)” 至Observer,使用Java内置的观察者模式可以使用推(push)或拉(pull)的方式传送数据。不同的是WeatherData现在扩展自Observable类,并继承到一些add、delete、notify观察者的方法。
Subject -> java.util.Observable(类)
Observer -> java.util.Observer(接口)

Observable如何送出通知?
首先需要利用扩展 java.util.Observable 接口产生“可观察者”类(想要进行通知,则必须调用Observable类的setChanged方法,但是Observable的setChanged方法为protected,故只能使用继承来实现自己的主题对象),然后:
- 调用setChanged(),标记状态已经改变的事实;
- 调用notifyObservers()中的一个:notifyObservers() 或 notifyObservers(Object arg)
Observer如何接收通知?
update(Observable o, Object arg) :主题Observable作为第一变量,好让观察者知道是哪个主题通知它的。Object arg正是传入notifyObservers(Object arg)的数据对象,如果没有说明则为空。
若想push数据给观察者,可以把数据作数据对象传送给notifyObservers(Object arg)方法。否则,观察者就必须从可观察者对象中pull数据。如何拉数据?
WeatherData.java
public class WeatherData extends Observable{
private float temperature;
private float humidity;
private float pressure;
public WeatherData(){ } //无需建立观察者列表ArrayList了
public void measurementsChanged(){
setChanged();//状态已经改变
notifyObservers();//pull
}
public void setMeasurements(float temp, float hum, float pre){
this.temperature = temp;
this.humidity = hum;
this.pressure = pre;
measurementsChanged();
}
public float getTemperature(){
return temperature;
}
public float gethumidity(){
return humidity;
}
public float getpressure(){
return pressure;
}
}
CurrentConditionDisplay.java
public class CurrentConditionsDisplay implements Observer, DisplayElement{
private float temperature;
private float humidity;
Observable observable;
public CurrentConditionsDisplay(Observable observable){
this.observable = observable;
observable.addObserver(this);
}
public void update(Observable o, Object arg){
if(o instanceof WeatherData){
WeatherData weatherData = (WeatherData)o;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.gethumidity();
display();
}
}
public void display(){
//输出
}
}
notice:
- 不想推送的时候,不调用setChanged()方法即可
- 通知顺序不依赖于注册的顺序(即主题通知观察者的顺序与添加观察者的顺序无关)
- setChanged()方法的必要性:若无,则温度计读数每十分之一度就会更新,造成WeatherData对象持续不断地通知观察者。若希望温差达到半度时更新,就调用setChanged()。有更多的弹性,更适当地通知观察者。
使用Java自带的观察者模式的缺点:
- Observable是一个类,而不是一个接口,导致Observable类的扩展性不高,不如自己实现的观察者模式灵活
- Observable将某些方法保护了起来(setChanged()和clearChanged()为protected),这意味着除非继承自Observable,否则将有关键的方法不能调用。导致无法通过组合的方式使其它类获得Observable类的功能。违反了设计原则“多用组合,少用继承”。
《Head First 设计模式》之观察者模式——天气显示的更多相关文章
- [JS设计模式]:观察者模式(即发布-订阅者模式)(4)
简介 观察者模式又叫发布---订阅模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 举一个现实生活中的例子,例如小 ...
- 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)
原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...
- 设计模式之观察者模式(Observable与Observer)
设计模式之观察者模式(Observable与Observer) 好久没有写博客啦,之前看完了<设计模式之禅>也没有总结一下,现在回忆一下设计模式之观察者模式. 1.什么是观察者模式 简单情 ...
- 8.5 GOF设计模式四: 观察者模式Observer
GOF设计模式四: 观察者模式Observer 现实中遇到的问题 当有许多不同的客户都对同一数据源感兴趣,对相同的数据有不同的处理方式,该如 何解决?5.1 定义: 观察者模式 观察者模式 ...
- php 设计模式之观察者模式(订阅者模式)
php 设计模式之观察者模式 实例 没用设计模式的代码,这样的代码要是把最上面那部分也要符合要求加进来,就要修改代码,不符合宁增不改的原则 介绍 观察者模式定义对象的一对多依赖,这样一来,当一个对象改 ...
- 实践GoF的23种设计模式:观察者模式
摘要:当你需要监听某个状态的变更,且在状态变更时通知到监听者,用观察者模式吧. 本文分享自华为云社区<[Go实现]实践GoF的23种设计模式:观察者模式>,作者: 元闰子 . 简介 现在有 ...
- java_设计模式_观察者模式_Observer Pattern(2016-07-27)
看了好几篇文章,最终还是觉得<Head First 设计模式>举得例子比较符合观察者模式. 观察者模式概述: 观察者模式有时被称作发布/订阅模式,它定义了一种一对多的依赖关系,让多个观察者 ...
- Head First 设计模式之观察者模式(Observer Pattern)
前言: 这一节开始学习观察者模式,开始讲之前会先像第一节那样通过一个应用场景来引入该模式.具体场景为:气象站提供了一个WeatherData对象,该对象可以追踪获取天气的温度.气压.湿度信息,Weat ...
- 设计模式学习——观察者模式(Observer Pattern)
0. 前言 观察者模式在许多地方都能够用到,特别是作为MVC模式的一部分,在MVC中,模型(M):存放数据,视图(V):显示数据.当模型中的数据发生改变时,视图会得到通知,这是典型的观察者模式. 1. ...
随机推荐
- Ubuntu下crontab命令的用法
cron是一个Linux下的后台进程,用来定期的执行一些任务.因为我用的是Ubuntu,所以这篇文章中的所有命令也只能保证在Ubuntu下有效,但其他系统应该也差不多. 想要让cron执行你指定的任务 ...
- 在Eclipse里面配置Struts2
下面介绍在Eclipse里面配置Struts2 下载Struts2的压缩包 我下载的是2.3.32版本 解压之后如图所示 apps目录:Struts2的范例 docs目录:Struts2的文档 lib ...
- 2. DVWA亲测命令执行漏洞
先看low级: 提示让我们输入一个IP地址来实现ping,猜测会是在系统终端中实现的, 我们正常输入127.0.0.1: 那我们就可以利用这个使用其他CMD命令 我们输入127.0.0.1& ...
- 7.27实习培训日志-Oracle SQL(三)
Oracle SQL(三) 视图 特性 简单视图 复杂视图 关联的表数量 1个 1个或多个 查询中包含函数 否 是 查询中包含分组数据 否 是 允许对视图进行DML操作 是 否 CREATE [OR ...
- 7.26实习培训日志-Oracle SQL(二)
Oracle SQL(二) 条件表达式 CASE 语句 或者DECODE 函数,两者均可实现 IF-THEN-ELSE 的逻辑,相比较而言,DECODE 更加简洁 SELECT last_name , ...
- Boost Python官方样例(三)
导出C++类(纯虚函数和虚函数) 大致做法就是为class写一个warp,通过get_override方法检测虚函数是否被重载了,如果被重载了调用重载函数,否则调用自身实现,最后导出的时候直接导出wa ...
- Java中的进制
byte bt = (byte) 254; System.out.println("bt = "+bt); /** * 之所以做这个与运算是为了把高位的数据去掉,准确的说是高位的1 ...
- [WIP]laravel 构成的概念
创建: 2019/06/21 生命周期 概论 检索service provider service container se ...
- 2014-11-3 NOIP模拟赛3
字符串 string.pas/c/cpp 1S/256MB [题目描述] 现在给一个字符串,你要做的就是当这个字符串中存在两个挨着的字符是相同的时就将这两个字符消除.需要注意的是,当把这两个字符消除后 ...
- cropper.js裁剪图片的使用
这两天难得有时间可以整理一下最近学习的东西,这两天项目中用到了头像上传裁剪的功能,这里只介绍头像的裁剪吧. 单独实现图片剪裁的功能还是挺容易的,入门级别的.看一遍官方给的文档,基本上就明白了.大家如果 ...