2.Observer Pattern(观察者模式)
Observer Pattern(观察者模式)定义:
在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。
干说定义肯定没有举例理解的透彻。想到Observer Pattern(观察者模式)就来举个生活中的例子来帮助我们更好消化和理解其具体含义。
举例:
订阅杂志或者报纸,这里面有两个主角,一个是报纸杂志的供应商(报社),一个是报纸杂志的订阅者。这里就是被观察者也叫主题(供应商)和观察者(订阅者)。主题(Subject)应该有观察者名单,当主题有新的报纸售出时将按主体持有的观察者名单一个一个发送新报纸(发送没有先后顺序,一切按存储顺序发送)。
同时主题还应该有三个方法:
一、将观察者写入名单中(registerObserver())
二、将观察者从名单中删除(removeObserver())
三、当有新消息发送及时通知名单中所有观察者(notifyObservers())
public interface Subject {//主题接口,所有报社都要实现该接口
/*没有存储订阅者的列表,是因为我们不想在接口中写死存储方式,
让编程人员自己在实现接口的时候写入想要的存储方式(如:链表,数组,栈,队列等)
这样更合理。*/
public void registerObserver(Observer o);//将订阅者登记在列表中
public void removeObserver(Observer o);//将订阅者从列表中移除
public void notifyObservers(Object arg);//有参通知方法,有新的消息即使通知列表中所有订阅者
public void notifyObservers();//无参通知方法
}
Subject
观察者所具有的东西就会少一些:
首先,内部需要有存储主题的对象,这样知道观察者所订阅的报社是哪一家,具有主题对象还有一个重要的原因,把登记、删除、通知观察者的功能全部委托给主题去做。
其次,还需要有更新自己消息的方法(update())新的消息发送过来,观察者也要及时更新自己内部消息,将旧的消息替换成新的消息。
public interface Observer{//所有订阅者要实现的接口
/*在接口中,没有主题对象,也是因为不想将主题对象写死在接口中,
在具体类中写入更好*/
public void update(Subject sub, Object args);//接收到新消息,及时更新
}
Observer
现在,我们来以具体的生活例子来介绍如何实现观察者模式(Observer Pattern):
有一家气象站,气象站本身已经具有WeatherData对象(相当于报社功能,可以获得目前的温度、湿度、气压三种数据)。
我们需要编写一个应用,该应用有很多种显示模式(从温度、湿度、气压中任选一到三个组合就是一种模式)。
当WeatherData对象获得最新的测量数据时,我们的应用可以及时更新显示模式中的数据。
根据要求写程序:
主题接口:
public interface Subject {//主题接口,所有报社都要实现该接口
/*没有存储订阅者的列表,是因为我们不想在接口中写死存储方式,
让编程人员自己在实现接口的时候写入想要的存储方式(如:链表,数组,栈,队列等)
这样更合理。*/
public void registerObserver(Observer o);//将订阅者登记在列表中
public void removeObserver(Observer o);//将订阅者从列表中移除
public void notifyObservers(Object arg);//有参通知方法,有新的消息即使通知列表中所有订阅者
public void notifyObservers();//无参通知方法
}
Subject
主题接口实体类:
import java.util.ArrayList;
public class WeatherData implements Subject{//气象站的实现类
private ArrayList observers;//观察者列表
private float temperature;//数据之一:温度
private float humidity;//数据之二:湿度
private float pressure;//数据之三:气压
private boolean status;//数据是否更新的标志
public WeatherData(){//初始化时,为观察者列表赋值
observers = new ArrayList();
}
public float getTemperature(){//获取温度的方法
return this.temperature;
}
public float getHumidity(){//获取湿度的方法
return this.humidity;
}
public float getPressure(){//获取气压的方法
return this.pressure;
}
public void registerObserver(Observer o){//将观察者记录在列表中
observers.add(o);
}
public void removeObserver(Observer o){//将观察者从列表中删除
int i = observers.indexOf(o);
observers.remove(i);
}
public void notifyObservers(Object args){//有参通知观察者方法
if(status){//判断数据是否有更新
for(int i = 0; i < observers.size(); i++){
Observer observer = (Observer) observers.get(i);
observer.update(this, args);
}
status = false;//消息发送成功后,将更新标志位重置
}
}
public void notifyObservers(){//无参通知观察者方法
notifyObservers(null);
}
public void setChange(){//数据是否更新的标志
status = true;
}
public void setMeasurements(float temperature, float humidity, float pressure){//数据更新方法
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public void measurementsChanged(){//数据改变后调用该方法
setChange();
notifyObservers();
}
}
WeatherData
观察者接口:
public interface Observer{//所有订阅者要实现的接口
/*在接口中,没有主题对象,也是因为不想将主题对象写死在接口中,
在具体类中写入更好*/
public void update(Subject sub, Object args);//接收到新消息,及时更新
}
Observer
显示更新数据的接口:
public interface DisplayElement{//显示更新数据的接口
public void display();
}
DisplayElement
观察者接口实体类:
public class CurrentConditionsDisplay implements Observer, DisplayElement{//显示当前温度、湿度的类
private WeatherData weatherData;//定义订阅的主题对象
private float temperature;//温度数据
private float humidity;//湿度数据
public CurrentConditionsDisplay(WeatherData weatherData){
this.weatherData = weatherData;
weatherData.registerObserver(this);//将该观察者对象登记在主题的观察者列表中
}
public void update(Subject sub, Object args){//数据更新方法
if(sub instanceof WeatherData){
WeatherData weatherData = (WeatherData) sub;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
public void display(){//显示数据的方法
System.out.println("Current conditions:" + temperature + "F degrees and " + humidity + "%humidity");
}
}
CurrentConditionsDisplay
测试类:
public class WeatherStation{
public static void main(String[] agrs){
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);//更新数据
weatherData.setMeasurements(82, 70, 29.2f);//更新数据
weatherData.setMeasurements(79, 90, 29.2f);//更新数据
}
}
WeatherStation
编译运行结果:

上面代码已经很完善了,而且不知道你有没有发现,其实每次WeatherData更新数据都是把所有数据都更新,但是我们的CurrentConditionsDispaly只获取温度和湿度两个数据,并且从来不获取多余的气压数据。这就是数据推送(Push)和数据抽取(Pull)的区别。
Push:不管你有没有订阅该数据,主题都会将该数据发送给订阅者,再由订阅者决定数据的取舍,没用的数据就不会记录在自己的数据中。
Pull:订阅者想要什么数据由订阅者说了算,主题只需提供获取数据的方法(get...())就好,而上面我们的气象站就是使用了数据抽取方式。
思想提炼:
1.多用组合,少用继承
2.为交互对象之间的松耦合设计而努力
2.Observer Pattern(观察者模式)的更多相关文章
- 设计模式(二)The Observer Pattern 观察者模式
问题引入 生成一个公告板显示当时的天气状况,当天气状况发生改变的时候公告板能够实时的更新. 模式定义 定义对象之间的一对多的依赖.当一个对象改变状态时,它的全部依赖者都会自己主动收到通知并自己主动更新 ...
- Observer pattern 观察者模式
一.认识观察者模式 我们看看报纸和杂志的订阅是怎么回事: 1.报社的业务就是出版报纸. 2.向某家报社订阅报纸,只要他们有新的报纸出版,就会给你送来,只要你是他们的订户,你就会一直收到报纸. 3.当你 ...
- 设计模式之 Observer Pattern 观察者模式
1.Subject通过一个容器保存零到多个Observer. 2.Subject通过Add,Delete方法调整Observer. 3.Subject的notifyObservers方法实际是逐个调用 ...
- Design Pattern: Observer Pattern
1. Brief 一直对Observer Pattern和Pub/Sub Pattern有所混淆,下面打算通过这两篇Blog来梳理这两种模式.若有纰漏请大家指正. 2. Use Case 首先我们来面 ...
- 设计模式复习小结一(Strategy Pattern/Observer Pattern/Decorator Patter/Factory Pattern)
目录: 前言 1. Stratrgy Pattern 2. Observer Pattern 3. Decorator Pattern 4. Factory Pattern 4.1 FactoryPa ...
- 深入浅出设计模式——观察者模式(Observer Pattern)
模式动机 建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应.在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而 ...
- 设计模式 - 观察者模式(Observer Pattern) 详细说明
观察者模式(Observer Pattern) 详细说明 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...
- 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)
原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...
- 设计模式 - 观察者模式(Observer Pattern) 详细解释
观察者模式(Observer Pattern) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...
- C#设计模式之十七观察者模式(Observer Pattern)【行为型】
一.引言 今天是2017年11月份的最后一天,也就是2017年11月30日,利用今天再写一个模式,争取下个月(也就是12月份)把所有的模式写完,2018年,新的一年写一些新的东西.今天我们开始讲& ...
随机推荐
- 201621123018《java程序设计》第13周作业总结
1. 本周学习总结 2. 为你的系统增加网络功能(购物车.图书馆管理.斗地主等)-分组完成 为了让你的系统可以被多个用户通过网络同时使用,需要为你的系统增加网络功能. 2.1 简述你想为你的系统增加什 ...
- Ubuntu18.04 - 实现鼠标右键新建文件功能!
Ubuntu18.04安装完毕后,你会发现,如果在桌面或其它地方,像在Windows下鼠标右键,新建一个文件,那么真的不行,没有那个选项!这个功能其实非常有用,怎么实现呢?新建一个你要右键新建类型文件 ...
- ScreenOper
/// <summary> /// 屏幕操作类 /// Add by 2017-07-25 /// 1.屏幕生成Image 方法 /// 2.Image按百分比压缩 方法 /// 3.Im ...
- Linux在终端和控制台下复制粘贴命令快捷键
1.在终端下: (1)复制命令:Ctrl + Shift + C 组合键. (2)粘贴命令:Ctrl + Shift + V 组合键. 2.在控制台下:(即vi编辑过程中) (1)复制命令:Ctrl ...
- Android初识Helloworld
在Eclipse+ADT中创建HelloWorld非常简单,直接按照导航下一步就可以了.本文重点不在如何创建,而在理解HelloWorld项目的文件. HelloWorld的目录结构有: src:存放 ...
- storm_分组策略
注意1:原始的案例 spout 和bolt都是1个并发 所以文件中30条日志 从spout发出以后 bolt接受到30条
- 自动化部署-从maven到shell脚本到jenkins
Java代码自动部署 [ ①Java代码自动部署-总结简介] 代码部署是每一个软件开发项目组都会有的一个流程,也是从开发环节到发布功能必不可少的环节.对于Java开发者来说,Java代码的发布部署也是 ...
- JavaScript中十种一步拷贝数组的方法
JavaScript中我们经常会遇到拷贝数组的场景,但是都有哪些方式能够来实现呢,我们不妨来梳理一下. 1.扩展运算符(浅拷贝) 自从ES6出现以来,这已经成为最流行的方法.它是一个很简单的语法,但是 ...
- SpringCloud入门之eclipse新建maven子项目和聚合项目
一.new maven project : next 二.勾选 create a simple project : next 三.Group Id:项目的包路径 如com.test,之后创建的C ...
- Tomcat学习总结(12)—— Tomcat集群配置
现目前基于javaWeb开发的应用系统已经比比皆是,尤其是电子商务网站,要想网站发展壮大,那么必然就得能够承受住庞大的网站访问量:大家知道如果服务器访问量过大,就会出现服应用务器崩溃的情况,这个时候怎 ...