zoukankan      html  css  js  c++  java
  • 设计模式之观察者模式(一)

    前面两篇已经带大家走进了设计模式的世界,了解了策略模式,还有基本的OO基础和OO原则,不知道你是否能读懂以及了解呢。接下来,我们就要进入第二个模式的学习了,观察者模式,让我们来一窥究竟吧。

    观察者模式是JDK中使用最多的模式之一,可以帮你的对象知悉情况,不会错过该对象感兴趣的事。对象甚至在运行时可决定是否要继续被通知。并且后续还会一并介绍一对多关系,以及松耦合。有了观察者,消息会变得更灵通。

    还是老样子,举例说明吧。有一个气象站,由WeatherData对象负责追踪目前的天气情况(温度、湿度、气压)。现在需要建立一个应用,有三种布告板,分别显示目前的状况、气象统计及简单的预报。当WeatherObject对象获得最新的测量数据时,三种布告板必须实时更新。重点就是实时更新,那我们就明白了,观察者模式是最好不过了。具体的情况如下图所示:

    image

    现在已经提供了WeatherData类,对比刚才的需求,来做进一步的分析。
    image
    我们知道些什么?

    • WeatherData类具有getter方法,可以取得三个测量值:温度、湿度和气压

    • 当新的测量数据备妥时,measurementsChanged()方法就会被调用

    • 我们需要实现三个使用天气数据的布告板:“目前状况”布告,“气象统计”布告,“天气预报”布告。一旦WeatherData有新的测量,这些布告必须马上更新

    • 此系统必须可扩展,让其他开发人员建立定制的布告板,用户可以随心所欲地添加或删除任何布告板。目前初始布告板有三类:“目前状况”布告、“气象统计”布告、“天气预报”布告

    如果按照上图提供的框架,我们在measurementsChanged()方法内部会有多个update的代码,因为我们有三个布告板,就会有三个冗余代码调用update,而且根据针对具体实现编程,会导致我们以后再增加或删除布告板时必须修改程序来达到目的。所以我们需要用观察者模式来进行优化这个流程。

    public void measurementsChanged() {
        float temp = getTemperature();
        float humidity = getHumidity();
        float pressure = getPressure();
        
        // 这里都是调用update方法
        currentConditionDisplay.update(temp,humidity,pressure);
        statisticsDisplay.update(temp,humidity,pressure);
        forecastDisplay.update(temp,humidity,pressure);
    }
    

    认识观察者模式

    观察者模式 = 出版者 + 订阅者
    就如同订阅报纸一样,你向某家报社订阅了报纸,只要他们有报纸出版,就会给你送一份过来。当你不想看报,不再订阅的时候,他们自然就不会继续为你送报纸,这个是用户主动的行为。主要报社还存在,你就可以订阅/取消订阅报纸,来达到观察的效果。

    所以,我们的观察者也是这么回事。我们在这里称之为主题(Subject)和观察者(Observer)。当主体内数据改变,就会通知观察者;观察者订阅主题,就能在主题数据更新时收到消息。如果对象已经取消订阅,那就失去了和主题的联系,独立开来,收不到消息。更形象点表达就用下图表示:

    image

    在真实世界中,观察者模式就会被定义成:

    观察者模式

    定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

    主题和观察者定义了一对多的关系。观察者依赖于此主题,只要主题状态一有变化,观察者就会被通知。根据通知风格,观察者可能因此新值而更新。

    总结成类图就是
    image

    这里还涉及到一个知识点,就是松耦合的概念。当两个对象之间松耦合,他们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计,让主题和观察者之间松耦合。这就是这节里面提及的一个设计原则:为了交互对象之间的松耦合设计而努力
    松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。

    所以重点来啦,刚才的气象站,你自己构想的设计图是如何的呢。我引用了书本中的设计图,快来看看你的,你们之间有何异同。
    image

    看到这里,其实可能很多人也都知道,Java内置的JDK是有观察者模式的支持的,我自己之前也用过,甚至说用的还行。但是这个我们放在下次讲解。这次,我们先自己动手,结合上面提到的特点,以及上面的类图来实现。提升下我们自己动手的能力。开始咯

    实现气象站

    public interface Subject {
    	// 注册观察者对象
    	public void registerObserver(Observer o);
    	// 删除观察者对象
    	public void removeObserver(Observer o);
    	// 当主题状态改变时,这个方法会被调用,以通知所有的观察者
    	public void notifyObservers();
    }
    
    /**
     * 
     * @Title: update
     * @Description: 当气象观测值改变时候,主题会把这些状态值当做方法的参数,传递给观察者
     * @param temp  温度
     * @param humidity  湿度
     * @param pressure 气压
     * @throws
     */
    public void update(float temp, float humidity, float pressure);
    
    /**
     * 
     * @Title:
     * @Description: DisplayElement 接口只包含一个方法。当布告板需要显示时,调用此方法
     * @Copyright:
     * @Company:
     * @author:XuYue
     * @version:Neon.3 Release (4.6.3)
     * @Create Date Time: 2019年3月26日 下午3:59:19
     * @Update Date Time: 2019年3月26日 下午3:59:19
     * @see
     */
    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();
    	}
    	
    	// 当注册观察者时,我们只要把它加到ArrayList中即可
    	@Override
    	public void registerObserver(Observer o) {
    		observers.add(o);
    	}
    
    	// 当观察者想取消注册,我们把它从ArrayList中删除
    	@Override
    	public void removeObserver(Observer o) {
    		int i = observers.indexOf(o);
    		if (i >= 0) {
    			observers.remove(i);
    		}
    	}
    
    	// 我们把状态告诉每一个观察者。因为观察者都实现了update(),所以我们知道如何通知它们
    	@Override
    	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 temperature,float humidity,float pressure) {
    		this.temperature = temperature;
    		this.humidity = humidity;
    		this.pressure = pressure;
    		measurementsChanged();
    	}
    
    }
    

    建立布告板

    /**
     * 
     * @Title:
     * @Description: 实现Observer接口,所以可以从WeatherData对象中获得改变
     * 也实现了DisplayElement接口,因为我们的API规定所有布告板都必须实现此接口
     */
    public class CurrentConditionDisplay implements Observer, DisplayElement {
    	
    	private float temperature;
    	private float humidity;
    	private Subject weatherData;
    	
    	public CurrentConditionDisplay(Subject weatherData) {
    		this.weatherData = weatherData;
    		weatherData.registerObserver(this);
    	}
    
    	// 当update()被调用时,我们把温度和湿度保存起来,然后调用display()
    	@Override
    	public void update(float temperature, float humidity, float pressure) {
    		this.temperature = temperature;
    		this.humidity = humidity;
    		display();
    	}
    
    	// 显示温度和湿度
    	@Override
    	public void display() {
    		System.out.println("Current conditions: " + temperature + " F degrees and " + humidity + "% humidity");
    	}
    }
    

    启动气象站

    public class WeatherStation {
    	public static void main(String[] args) {
    		// 建立有一个WeatherData对象
    		WeatherData weatherData = new WeatherData();
    		
    		// 建立布告板,把WeatherData对象传给它们
    		CurrentConditionDisplay currentConditionDisplay = new CurrentConditionDisplay(weatherData);
    		
    		weatherData.setMeasurements(80, 65, 30.4f);
    	}
    }
    
    // 结果
    Current conditions: 80.0 F degrees and 65.0% humidity
    

    至此,我们根据一系列的流程,自己动手实现了气象站的观察者模式,是不是很酷,是不是觉得自己很棒,给自己鼓个掌吧。

    观察者模式的第一部分就先到这里,我们从拿到题材,到理解观察者模式,再到设计类图,最后到功能实现,都是一步一个脚印,踏踏实实地在走。文中说的不明白的地方,可以继续讨论完善。这次学到了一个设计模式,一个设计原则,又一次巩固了类图的画法,小伙伴们觉得有收获吗?

    观察者模式的下篇,会继续完善这个模式,用Java自带的观察者进行实现,并对观察者作出总结。我们下次再会。

    PS:代码已经上传,需要查看的朋友点击此处HeadFirstDesign

    爱生活,爱学习,爱感悟,爱挨踢

    image

  • 相关阅读:
    Windows Phone 7 开发之检查手机网络
    还原ipa里的png图片资源
    phpmyadmin 自动登录的办法
    Composer 是什么
    vuejs 和 element 搭建的一个后台管理界面
    Vue2 后台管理系统解决方案
    解决无限 This file is indented with tabs instead of 4 spaces
    无聊的小知识 规格严格
    va_list!!! 规格严格
    Java动态编译一个简单的例子(我转载的,但是经过修定,可以在Eclipse下运行) 规格严格
  • 原文地址:https://www.cnblogs.com/dimple91/p/10606420.html
Copyright © 2011-2022 走看看