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. 【译】准备好你求职时候用的 GitHub 账号

    我目前正在招聘,很多人分享了他们的GitHubs个人资料和项目,但是维护得很差,所以我决定为活跃的求职者写一个小指南. 无论是否合理,技术招聘人员倾向于从您的GitHub个人资料中推断出很多关于您的信 ...

  2. Spring Cloud实践之服务注册与发现Eureka

    一.简述: 服务提供者producer与服务消费者consumer都注册到eureka server,然后服务consumer在其应用内直接调用producer的服务名来调用服务,而不是像之前一样调用 ...

  3. jQuery中FormData的使用

    web中数据提交事件是常常发生的,但是大多数情况下我们不希望使用html中的form表单提交,因为form表单提交会中断当前浏览器的操作并且会调到另一个地址(即使这个地址是当前页面),并且会重复加载一 ...

  4. vue.js 的起步

    vue.js 的起步 转载 作者:伯乐在线专栏作者 - 1000copy 点击 → 了解如何加入专栏作者 如需转载,发送「转载」二字查看说明 介绍 vue.js 是一个客户端js库,可以用来开发单页应 ...

  5. Android 9 新功能 及 API 介绍(提供了实用的模块化的功能支持,包括 人工智能)

      Android 9(API 级别 28)为用户和开发者引入了众多新特性和新功能. 本文重点介绍面向开发者的新功能. 要了解新 API,请阅读 API 差异报告或访问 Android API 参考. ...

  6. 跨站脚本攻击(xss)理解

    一  概念 攻击者不直接攻击受害者,而是利用受害者登陆的网站中的漏洞,对受害者进行攻击. 二  危害 由于js本身的限制,并不能直接对用户的电脑造成侵害,但是可以: 1. 获取用户的storage,c ...

  7. 没啥事用C语言写一个Trie tree玩玩,支持中英文,用g++编译通过

    #include <cstdio> #include <cstdlib> #include <vector> #define ALPHABETS 2600000 # ...

  8. 分析NonfairSync加锁/解锁过程

    类继承关系: NonfairSync => Sync => AbstractQueuedSynchronizer 类NonfairSync final void lock() { if ( ...

  9. logstash安装与logstash-input-jdbc插件使用

    ElasticSearch的索引可以手动添加索引的,就是类似下面这样添加的 PUT /movies/movie/1 { "title": "The Godfather&q ...

  10. 移动键盘 滚动input

    window.addEventListener('resize', function () { if(document.activeElement.tagName === 'INPUT'){ docu ...