观察者模式(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,故只能使用继承来实现自己的主题对象),然后:

  1. 调用setChanged(),标记状态已经改变的事实;
  2. 调用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:

  1. 不想推送的时候,不调用setChanged()方法即可
  2. 通知顺序不依赖于注册的顺序(即主题通知观察者的顺序与添加观察者的顺序无关)
  3. setChanged()方法的必要性:若无,则温度计读数每十分之一度就会更新,造成WeatherData对象持续不断地通知观察者。若希望温差达到半度时更新,就调用setChanged()。有更多的弹性,更适当地通知观察者。

使用Java自带的观察者模式的缺点:

  1. Observable是一个类,而不是一个接口,导致Observable类的扩展性不高,不如自己实现的观察者模式灵活
  2. Observable将某些方法保护了起来(setChanged()和clearChanged()为protected),这意味着除非继承自Observable,否则将有关键的方法不能调用。导致无法通过组合的方式使其它类获得Observable类的功能。违反了设计原则“多用组合,少用继承”。

《Head First 设计模式》之观察者模式——天气显示的更多相关文章

  1. [JS设计模式]:观察者模式(即发布-订阅者模式)(4)

    简介 观察者模式又叫发布---订阅模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 举一个现实生活中的例子,例如小 ...

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

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

  3. 设计模式之观察者模式(Observable与Observer)

    设计模式之观察者模式(Observable与Observer) 好久没有写博客啦,之前看完了<设计模式之禅>也没有总结一下,现在回忆一下设计模式之观察者模式. 1.什么是观察者模式 简单情 ...

  4. 8.5 GOF设计模式四: 观察者模式Observer

    GOF设计模式四: 观察者模式Observer  现实中遇到的问题  当有许多不同的客户都对同一数据源感兴趣,对相同的数据有不同的处理方式,该如 何解决?5.1 定义: 观察者模式  观察者模式 ...

  5. php 设计模式之观察者模式(订阅者模式)

    php 设计模式之观察者模式 实例 没用设计模式的代码,这样的代码要是把最上面那部分也要符合要求加进来,就要修改代码,不符合宁增不改的原则 介绍 观察者模式定义对象的一对多依赖,这样一来,当一个对象改 ...

  6. 实践GoF的23种设计模式:观察者模式

    摘要:当你需要监听某个状态的变更,且在状态变更时通知到监听者,用观察者模式吧. 本文分享自华为云社区<[Go实现]实践GoF的23种设计模式:观察者模式>,作者: 元闰子 . 简介 现在有 ...

  7. java_设计模式_观察者模式_Observer Pattern(2016-07-27)

    看了好几篇文章,最终还是觉得<Head First 设计模式>举得例子比较符合观察者模式. 观察者模式概述: 观察者模式有时被称作发布/订阅模式,它定义了一种一对多的依赖关系,让多个观察者 ...

  8. Head First 设计模式之观察者模式(Observer Pattern)

    前言: 这一节开始学习观察者模式,开始讲之前会先像第一节那样通过一个应用场景来引入该模式.具体场景为:气象站提供了一个WeatherData对象,该对象可以追踪获取天气的温度.气压.湿度信息,Weat ...

  9. 设计模式学习——观察者模式(Observer Pattern)

    0. 前言 观察者模式在许多地方都能够用到,特别是作为MVC模式的一部分,在MVC中,模型(M):存放数据,视图(V):显示数据.当模型中的数据发生改变时,视图会得到通知,这是典型的观察者模式. 1. ...

随机推荐

  1. OpenCV 鼠标手动绘制掩码图像

    OpenCV 鼠标手动绘制掩码图像 完整的代码: #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui ...

  2. Entity Framework Code-First(12):Configure One-to-Many

    Configure One-to-Many Relationship: Here, we will learn how to configure One-to-Many relationship be ...

  3. 6.7 root和user之间的切换

  4. Struts2学习第七课 ActionSupport

    com.opensymphony.xwork2.ActionSupport类是默认的Action类,如果某个Action节点没有配置class属性,则ActionSupport即为待执行的Action ...

  5. Umbraco安装过程中出现的问题以及调试

    在VS2015中使用NuGet安装完UmbracoCms后,按Ctrl+F5运行程序来完成安装UmbracoCms的过程中,发现一直在安装但是没有反应 估计是出现了错误.所以我到项目所在的文件夹中查找 ...

  6. 《鸟哥的Linux私房菜》读书笔记5

    1.shell script 用在系统管理上面是很好的一项工具,但是用在处理大量数值运算上, 就不够好了; 2.shell script 其实就是纯文字文件 (ASCII) ,我们可以编辑这个档案, ...

  7. 【mybatis 的foreach的用法】

    foreach一共有三种类型,分别为List,[](array),Map三种. foreach属性 属性 描述 item 循环体中的具体对象.支持属性的点路径访问,如item.age,item.inf ...

  8. cinder介绍及使用lvm本地存储

    1.cinder简介 Cinder提供持久的块存储,目前仅供给虚拟机挂载使用.它并没有实现对块设备的管理和实际服务,而是为后端不同的存储结构提供了统一的接口,不同的块设备服务厂商在 Cinder 中实 ...

  9. 洛谷P1342 请柬

    P1342 请柬 题目描述 在电视时代,没有多少人观看戏剧表演.Malidinesia古董喜剧演员意识到这一事实,他们想宣传剧院,尤其是古色古香的喜剧片.他们已经打印请帖和所有必要的信息和计划.许多学 ...

  10. 简谈react中的虚拟DOM

    相信你在看到此篇前也翻阅大量的对DOM的文章讲解和介绍 react中的虚拟DOM 此篇我尽量说人话(大白话),不然想必你在看到别的大神的文章早就懂了. 不说废话了,上干货. 1.首先简单对Html中的 ...