最近在读《Head First设计模式》一书,此系列会引用源书内容,但文章内容会更加直接,以及加入一些自己的理解。

  观察者模式(有时又被称为模型-视图(View)模式、源-收听者(Listener)模式或从属者模式)。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

  引子

  我们的公司刚刚你拿下了一个中国气象局的一个招标,负责建立北京市的气象观测站。该气象站必须建立在中国气象局专利申请中的WeatherData对象上,有WeatherData对象负责追踪目前的天气状况,(由于本文只做例子,只追踪温度、湿度、气压)。气象站提出应用的要求:

  1、我们公司能建立一个应用,有三种布告板,可以显示实时天气状况、气象统计、以及简单的预报。

  2、当WeatherData对象获得最新的测量数据时,三种布告板必须实时更新。

  3、我们的气象站是可以扩展的,气象站希望可以公布一组API(接口),好让其他开发人员可以自己写出自己的气象公告板,并插入此应用

  初步设计

  1、首先根据气象站的需求,我们画一个示意图。

  

  WeatherData对象知道如何跟物理气象站联系,当数据跟新时,WeatherData对象从气象站获得数据,WeatherData对象随机更新三个布告板的显示:目前状况(温度、湿度、气压)、气象统计和天气预报,所以我们目前的主要任务是,利用WeatherData对象从气象站获取数据,并跟新三个布告板。

  在设计之前,我们要看一看气象站给我们WeatherData类(此处只做示意,代码后面会变得完整)

public class WeatherData {
//下面的三个方法获取温度、湿度、以及压力
public float getTemperature(){
temperature=获取温度;
return temperature;
}
public float getHumidity(){
temperature=获取湿度;
return humidity;
}
public float getPressure(){
temperature=获取压力;
return pressure;
}
//一旦气象测量更新、此方法就会被调用
public void measurementsChanged(){ }

  好了,到此为止我们已经知道了最终的设计要求以及气象站给我们的WeatherData类,下面我们就要开始设计我们的应用,根据我们知道的东西,我们可以先来一个不完整的初版,我们现在WeatherData中增加一些方法(不着急先写出方法的具体内容,只要写出一个方法名,记得它的功能即可)

public class WeatherData {    
//下面的三个方法获取温度、湿度、以及压力
public float getTemperature(){
temperature=获取温度;
return temperature;
}
public float getHumidity(){
temperature=获取湿度;
return humidity;
}
public float getPressure(){
temperature=获取压力;
return pressure;
}
//一旦气象测量更新、此方法就会被调用
public void measurementsChanged(){
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();      currentConditionsDisplay.update(temp,humidity,pressure);
     statisticsDiaplay.updata(temp,humidity,pressure);
     forecastDisplay.update(temp,humidity,pressure);
}

  这是我们第一次设计的程序,由于我们已经开始准备用设计模式了,所以我们可以看看我们目前的代码中是不是有还可以改进的地方,首先,我们看到了三个公告板都有update方法,那我们就可以把这个方法

抽取成一个接口,这样我们每次增加公告板时直接实现此接口就可以,其次,本次我们讨论的是观察者模式,那我们的引子就先说到这,我们开始看看什么是观察者设计模式,我们了解完观察者设计模式后,我们

再回到我们的引子,完成设计。

  认识观察者设计模式

  在生活中,订阅报纸就是就是一个观察者设计模式,报社的业务就是出版报纸,当你订阅了某家报社的报纸,只要报社有了新的报纸出版,他们就会给你送来,只要你是他们的订户,你就一定会收到报

纸,当你不想在看这家报纸时,你就可以取消订阅,他们就不会再送来新报纸。其实,出版社+订阅者就等于观察者模式,只是在这个模式里,出版社叫做“主题(Subject)”,订阅者叫做观察者"(Observer)”,如果

你还没有看懂,那我们来看下面这个图:

  

  图中主题对象管理者int数据,狗对象、猫对象、老鼠对象订阅了主题,一旦主题对象发生改变,就会给这些订阅了的对象发送数据,鸭子对象没有订阅主题,就不会接收到数据。

  有一天,鸭子对象突然对主题对象管理的数据起了兴趣,它就需要过去告诉主题,我对你的数据改变有兴趣,一有变化请告诉我,这就是“订阅”的过程,在这之后,鸭子对象就是正式

的观察者了,以后主题管理的数据再有改变就会告诉鸭子对象。

  又有一天,老鼠对象对主题说,你管理的数据我观察太久了,我不想再当你的观察者了,这时主题就删掉老鼠对象,每当数据再变化时,老鼠对象就不会接收到数据。

  观察者设计模式定义了对象之间的一对多依赖,这样的依赖,当一个对象改变时,它的所有依赖者都会受到通知并自动更新

  下面是便是定义观察者设计模式的类图:

  

  观察者设计模式应用到我们的气象站中

  我们已经基本了解了观察者设计模式,那我们就可以回到我们的引子完成原来的设计

  初步思考

  通过我们在上面分析的观察者设计模式,我们发现这是模式是一对多的关系,每当一改变时,就会给多发送改变的数据,而在我们的气象站设计中,正是一对多的关系,一就是我们的WeatherData数据

多就是显示的布告板,而不同的布告板有相同和不同的方法,我们可以把相同的部分抽取成接口,实现代码的复用,那么下面就让我们参照上面观察者设计模式的类图画一下我们气象站的设计类图。(自己

先尝试一下哟)

  设计气象站

  

    实现气象站

    根据上面的类图,我们就可以完善我们的代码啦

    Subject类的接口及WeatherData实现

package com.frank.observe.subject;
import com.frank.observe.observer.Observer; public interface Subject {
  //主题注册、删除、通知观察者方法
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
//信息改变时执行方法
public void measurementsChanged();
}
package com.frank.observe.subject;
import java.util.ArrayList;
import com.frank.observe.observer.Observer; public class WeatherData implements Subject {
  //WeatherData实现了Subject方法
private ArrayList<Observer> observers;
  //包含温度、湿度、压力
private float temperature;
private float humidity;
private float pressure;
//构造器创建观察者们的集合
public WeatherData(){
observers = new ArrayList<Observer>();
}
//接口方法
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if(i>=){
observers.remove(o);
}
}
@Override
public void notifyObserver() {
for(int i = ;i<observers.size();i++){
Observer observer = observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
@Override
public void measurementsChanged() {
notifyObserver();
}
  //WeatherData自己的set方法,一会测试时在test中改变数据源
public void setMeasurements(float temperature,float humidity, float pressure){
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
measurementsChanged();
} }

   Observer类的接口以及CurrentConditionDisplay实现

package com.frank.observe.observer;
//Observer接口中更新数据的方法
public interface Observer {
public void update(float temp,float humidity,float pressure);
}
package com.frank.observe.observer;
import com.frank.observe.display.DisplayElement;
import com.frank.observe.subject.Subject;
import com.frank.observe.subject.WeatherData;
//显示板要实现Observer以及DisplayElement接口(及公共方法)
public class CurrentConditionDisplay implements Observer, DisplayElement {
private float temprature;
private float humidity;
  //将weatherData作为自己的成员变量
private Subject weatherData;
//在带参构造器中将自己注册到weatherData中
public CurrentConditionDisplay(Subject weatherData){
this.weatherData=weatherData;
weatherData.registerObserver(this);
}
  //接口的更新方法
@Override
public void update(float temp, float humidity, float pressure) {
this.temprature=temp;
this.humidity=humidity;
     //接口的显示方法
display(); }
@Override
public void display() {
System.out.println("current conditions" +temprature+"F degrees and"+humidity+"% humidity"); } }

  DisplayElement接口

package com.frank.observe.display;

public interface DisplayElement {
public void display();
}

  Test方法

package com.frank.observe.test;
import com.frank.observe.observer.CurrentConditionDisplay;
import com.frank.observe.subject.WeatherData; public class Test {
public static void main(String[] args) {
     //新建一个主题
WeatherData weatherData = new WeatherData();
     //新建一个公告板并将自己注册到主题中
CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay(weatherData);
     //每当主题更新数据时发送给观察者,观察者显示在公告板中
weatherData.setMeasurements(80, 60, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
} }

  结果

current conditions80.0F degrees and60.0% humidity
current conditions82.0F degrees and70.0% humidity
current conditions78.0F degrees and90.0% humidity

  总结

   要点:

OO基础:抽象

OO原则:封装变化、多用组合,少用继承、针对接口编程,不针对实现编程

OO模式:观察者设计模式----在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会受到通知,并自动更新(后面的系列会看到其代表人物--MVC)

  

《Head First设计模式》批注系列(一)——观察者设计模式的更多相关文章

  1. Net设计模式实例系列文章总结

    1 什么是设计模式 设计模式是对在软件设计过程中重复出现的问题提出了一种比较好的解决方案.正如一位专家所说:设计模式是对程序设计人员经常遇到的设计问题的可再现的解决方案(The Smalltalk C ...

  2. C#基础系列——委托和设计模式(二)

    前言:前篇 C#基础系列——委托实现简单设计模式 简单介绍了下委托的定义及简单用法.这篇打算从设计模式的角度去解析下委托的使用.我们知道使用委托可以实现对象行为(方法)的动态绑定,从而提高设计的灵活性 ...

  3. 【翻译】设计模式学习系列1---【Design Patterns Simplified: Part 1【设计模式简述:第一部分】】

    原文链接:http://www.c-sharpcorner.com/UploadFile/19b1bd/design-patterns-simplified-part1/ Design Pattern ...

  4. 设计模式学习系列6 原型模式(prototype)

    原型模式(prototype)用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.允许一个对象再创建另外一个新对象的时候根本无需知道任何创建细节,只需要请求圆形对象的copy函数皆可. 1 ...

  5. (java)从零开始之--观察者设计模式Observer

    观察者设计模式:时当一个对象发生指定的动作时,要通过另外的对象做出相应的处理. 步骤: 1. A对象发生指定的动作是,要通知B,C,D...对象做出相应的处理,这时候应该把B,C,D...对象针对A对 ...

  6. Unity 3D观察者设计模式-C#委托和事件的运用

    C#观察者设计模式 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享.心创新! ...

  7. IOS设计模式第七篇之观察者设计模式

    版权声明:原创作品,谢绝转载!否则将追究法律责任. 观察者设计模式 在观察者设计模式里面,一个对象通知其他的对象一些状态的改变.涉及这些对象不需要知道另一个对象---因此鼓励解耦设计模式.这个设计模式 ...

  8. 《Kubernetes与云原生应用》系列之容器设计模式

    http://www.infoq.com/cn/articles/kubernetes-and-cloud-native-app-container-design-pattern <Kubern ...

  9. javaEE之--------统计站点在线人数,安全登录等(观察者设计模式)

    整体介绍下:  监听器:监听器-就是一个实现待定接口的普通Java程序,此程序专门用于监听别一个类的方法调用.都是使用观察者设计模式. 小弟刚接触这个,做了些简单的介绍.大神请绕道,技术仅仅是一点点, ...

随机推荐

  1. Android-Java-子类实例化过程(内存图)

    案例一: package android.java.oop15; // 描述Person对象 class Person { // 构造方法就算不写 默认有一个隐式的无参构造方法:public Pers ...

  2. 微服务日志之.NET Core使用NLog通过Kafka实现日志收集

    一.前言 NET Core越来越受欢迎,因为它具有在多个平台上运行的原始.NET Framework的强大功能.Kafka正迅速成为软件行业的标准消息传递技术.这篇文章简单介绍了如何使用.NET(Co ...

  3. 【算法】实现字典API:有序数组和无序链表

    参考资料 <算法(java)>                           — — Robert Sedgewick, Kevin Wayne <数据结构>       ...

  4. Vue自定义指令,ref ,sync,slot

    一.自定义指令 vue中可以自己设置指令,通过directive来实现,有2种创建方式,一种是局部创建,一种是全局创建. 第一种:局部创建 如果想注册局部指令,组件中也接受一个 directives  ...

  5. PHP-1安装配置

    php-fpm启动 /usr/local/php/sbin/php-fpm start

  6. 使用 Maven 插件将 class(字节码文件),resource(资源文件),lib(依赖的jar包)分开打包

    1. 在pom文件中对各个插件进行配置 <?xml version="1.0" encoding="UTF-8"?> <project xml ...

  7. linux下i2c的驱动架构分析和应用

    i2c在linux下的代码在/driver/i2c下面,总体代码如下所示: i2c-core.c 这个文件实现了I2C核心的功能以及/proc/bus/i2c*接口.    i2c-dev.c  实现 ...

  8. druid的关键参数+数据库连接池运行原理

      minEvictableIdleTimeMillis :连接保持空闲而不被驱逐的最长存活时间.(默认30分钟) Destory线程中如果检测到当前连接的最后活跃时间和当前时间的差值大于minEvi ...

  9. [转]KMP算法理解及java实现

    这大概是我看的最好懂的KMP算法讲解了,不过我还只弄懂了大概思想,算法实现我到时候用java实现一遍 出处:知乎 https://www.zhihu.com/question/21923021/ans ...

  10. Python3学习笔记 - day1

    前言 本文不是一篇系统的从零开始学习Python的教程,如果你需要从零开始学习Python,廖雪峰的官方网站中Python教程这部分将是比较好的一种选择,如果你英语比较好,也可以在国外的一些网站上找到 ...