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(观察者模式)的更多相关文章

  1. 设计模式(二)The Observer Pattern 观察者模式

    问题引入 生成一个公告板显示当时的天气状况,当天气状况发生改变的时候公告板能够实时的更新. 模式定义 定义对象之间的一对多的依赖.当一个对象改变状态时,它的全部依赖者都会自己主动收到通知并自己主动更新 ...

  2. Observer pattern 观察者模式

    一.认识观察者模式 我们看看报纸和杂志的订阅是怎么回事: 1.报社的业务就是出版报纸. 2.向某家报社订阅报纸,只要他们有新的报纸出版,就会给你送来,只要你是他们的订户,你就会一直收到报纸. 3.当你 ...

  3. 设计模式之 Observer Pattern 观察者模式

    1.Subject通过一个容器保存零到多个Observer. 2.Subject通过Add,Delete方法调整Observer. 3.Subject的notifyObservers方法实际是逐个调用 ...

  4. Design Pattern: Observer Pattern

    1. Brief 一直对Observer Pattern和Pub/Sub Pattern有所混淆,下面打算通过这两篇Blog来梳理这两种模式.若有纰漏请大家指正. 2. Use Case 首先我们来面 ...

  5. 设计模式复习小结一(Strategy Pattern/Observer Pattern/Decorator Patter/Factory Pattern)

    目录: 前言 1. Stratrgy Pattern 2. Observer Pattern 3. Decorator Pattern 4. Factory Pattern 4.1 FactoryPa ...

  6. 深入浅出设计模式——观察者模式(Observer Pattern)

    模式动机 建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应.在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而 ...

  7. 设计模式 - 观察者模式(Observer Pattern) 详细说明

    观察者模式(Observer Pattern) 详细说明 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...

  8. 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)

    原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...

  9. 设计模式 - 观察者模式(Observer Pattern) 详细解释

    观察者模式(Observer Pattern) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...

  10. C#设计模式之十七观察者模式(Observer Pattern)【行为型】

    一.引言   今天是2017年11月份的最后一天,也就是2017年11月30日,利用今天再写一个模式,争取下个月(也就是12月份)把所有的模式写完,2018年,新的一年写一些新的东西.今天我们开始讲& ...

随机推荐

  1. Android开发教程 - 使用Data Binding(四)在Fragment中的使用

    本系列目录 使用Data Binding(一)介绍 使用Data Binding(二)集成与配置 使用Data Binding(三)在Activity中的使用 使用Data Binding(四)在Fr ...

  2. SQL注入之PHP-MySQL实现手工注入-字符型

    SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令.具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎 ...

  3. 跟着刚哥学习Spring框架--Spring容器(二)

    Spring容器 启动Spring容器(实例化容器) -- IOC容器读取Bean配置创建Bean实例之前,必须对它进行实例化(加载启动),这样才可以从容器中获取Bean的实例并使用.  Bean是S ...

  4. Vue过渡动画—Vue学习笔记

    要求:要通过Vue使用过渡动画我们要把需要过度的元素放在<transition name='变量名'></transition>中. 原理:通过在特定的时刻增加/移除样式实现. ...

  5. Python中Flask框架SQLALCHEMY_ECHO设置

    在用配置类的方式给app设置配置时, SQLALCHEMY_ECHO 这个是记录打印SQL语句用于调试的, 一般设置为False, 不然会在控制台输出一大堆的东西 /home/python/.virt ...

  6. sync.WaitGroup和sync.Once

    sync.WaitGroup,顾名思义,等待一组goroutinue运行完毕.sync.WaitGroup声明后即可使用,它有如下方法: func (wg *WaitGroup) Add(delta ...

  7. [Umbraco] document type里的父节点与子节点的设置

    虽然我们不能像做数据库设计那样建立主外键关系.但我们建立xml里父子关系,父子关系其实是指是否允许在一个页面(如频道,分类,栏目等)下创建子页面,这就相当于建立站点的树状结构,对于筛选数据会有很大的作 ...

  8. 【原创】Your Connection is not private

    用Chrome打开google等https网站时碰到问题: “your connection is not private”. 后来发现是跟GoAgent的安全证书有关系(我用XX.NETFQ) 解决 ...

  9. (转)【学习笔记】通过netstat+rmsock查找AIX端口对应进程

    原文:http://www.oracleplus.net/arch/888.html https://www.ibm.com/support/knowledgecenter/zh/ssw_aix_72 ...

  10. (转)python的ConfigParser模块

    原文:https://blog.csdn.net/miner_k/article/details/77857292 如何使用Python3读写INI配置文件-------https://blog.cs ...