1
继续我们的设计模式学习,有个好的“观察者”可以让你开发效率大大提高

直接进入正题,我们用一个气象站程序来模拟此模式。
有一个气象站程序,能对湿度,温度,气压进行监测并显示在“显示”装置上面
模拟图如下,此系统中有三个部分
气象站:获取实际气象数据的装置
WeatherData对象:用来追踪气象站发出的数据,并实时更新到布告栏中
布告栏:显示目前天气状况给用户看的

图片描述

换个比方来说说什么是观察者模式~~~~
1:报社的业务就是出版报纸。
2:向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来,只要你是他们的客户,你就会一直收到新报纸。
3:当你不想再看报纸的时候,取消订阅,报社就不会给你送新报纸了。
4:只要报社一直存在,那么就一直会有人来订阅或取消订阅报纸的服务。
懂了吗?出版社+订阅者=观察者模式,只不过用在“观察者”中,出版社成为“主题”(Subject),订阅者成为“观察者”(Observer)

OK,让我用视图更好的说明
图片描述

鸭子要加入了
图片描述

目前只要主题那边更新数据,在观察者这边就都能获得数据了。
图片描述

此时狗对象不想当观察者了,它向“主题”提出了“离开”的意愿
图片描述

狗对象离开了,当动物(主题)对象更新数据时,狗对象是不会更新的,它已经不是观察者了,但同时它也可以回来重新“注册”成观察者。
图片描述

自此观察者模式的核心流程就是这样了,代码先不急着写,先思考下。
观察者模式定义:定义了对象之间一对多依赖,这么一来,当一个对象改变状态时,它的所有依赖着都会收到自动更新。
图片描述

这样的设计使得主题和多个观察者之间的依赖性降低了,Subject只管“交互”Observer,至于具体怎么实现内容或其他细节内容,Subject不需要知道,它只知道指向Observer就可以了。任何时候我们都可以增加新的观察者,因为主题唯一依赖的东西是一个实现Observer接口的实现类,即使我们随意增加删除观察者都不会影响主题,我们可以独立地复用主题或观察者,如果我们在其他地方需要使用主题或观察者,可以轻易复用,因为二者并非紧耦合。

设计原则一:所做的一切都是为了对象之间能松耦合。(降低依赖度)
注:松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的相互依赖降到最低。

主题接口

package Interface;

/**
 * 设计模式:观察者模式 主题和观察者之间有一对多关系
 * 
 * @author Joy
 * 
 */
// 主题接口
public interface Subject {
    // 这两个方法都需要一个观察者作为变量(参数设为观察者),该观察者是用来注册和删除的
    public void registerObserver(Observer o);

    public void removeObserver(Observer o);

    // 当主题状态改变时调用这个方法,作用是通知观察者
    public void notifyObserver();

}

观察者接口

package Interface;

//观察者接口
public interface Observer {
    //当气象观测数据改变时,主题会把新的状态数据已参数形式传递给观察者
    public void update(float temp, float humidity, float pressure);
}

显示接口(显示数据)

package Interface;
public interface DisplayElement {
    public void display();
}

气象站类(主题实现类)

package Implements;

import java.util.ArrayList;

import Interface.Observer;
import Interface.Subject;

//气象站实现主题接口
//一个气象站(主题)可以更新多个显示(观察者)面板值
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);
        
    }
    

    // 当观察者想取消注册时,从list中移除即可
    @Override
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if (i > 0) {
            observers.remove(i);
        }
    }

    @Override
    public void notifyObserver() {
        for (int i = 0; i < observers.size(); i++) {
            //建立观察者对象
            Interface.Observer observer = (Interface.Observer) observers.get(i);
            //将数据更新至观察者对象内
            observer.update(temperature, humidity, pressure);    
            System.out.println("主题更新了数据,观察者那同步更新");
        }
    }

    // 当从气象站得到更新观测值时,我们通知观察者
    public void measurementsChanged() {
        notifyObserver();
    }

    //设置温度,湿度,压力值
    public void setMeasurements(float temperature, float humidity,
            float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }



    
}

下面是两个观察者实现类,气温湿度显示装置和天气预报装置

package Implements;

import Interface.DisplayElement;
import Interface.Observer;
import Interface.Subject;
//观察者实现类
//气温,湿度显示装置
public class CurrentConditionDisplay implements Observer,DisplayElement {
    private float temperature;
    private float humidity;
    private Subject weatherData;
    
    //构造器需要weatherData对象,用于注册
    public CurrentConditionDisplay(Subject weatherData){
        this.weatherData=weatherData;
        //注册
        weatherData.registerObserver(this);
    }

    @Override
    public void display() {
        System.out.println("温度板");
        System.out.println("温度:"+temperature+"\t"+"湿度:"+humidity);        
    }

    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature=temperature;
        this.humidity=humidity;
        //当update调用时,将温度湿度数据保存起来然后调用display
        display();        
    }
}

天气预报类

package Implements;

import Interface.DisplayElement;
import Interface.Observer;

/**
 * 天气预报装置
 * 
 * @author Joy
 * 
 */
public class ForecastDisplay implements Observer, DisplayElement {
    private float currentPressure = 29.92f; // 当前压力值(赋个初始值)
    private float lastPressure;// 上一次压力值
    private WeatherData weatherData;

    public ForecastDisplay(WeatherData weatherData) {
        this.weatherData = weatherData;
        // 注册成观察者
        weatherData.registerObserver(this);
    }

    @Override
    public void update(float temp, float humidity, float pressure) {
        lastPressure = currentPressure;
        //pressure传过来的压力值
        currentPressure = pressure;
        display();
    }

    @Override
    public void display() {
        System.out.print("天气预报: ");
        if (currentPressure > lastPressure) {
            System.out.println("天气晴朗");
        } else if (currentPressure == lastPressure) {
            System.out.println("这次天气状况与上一次相同");
        } else if (currentPressure < lastPressure) {
            System.out.println("注意降温,阴云多雨天气");
        }
    }
}

测试类

package TestMain;



import Implements.CurrentConditionDisplay;
import Implements.ForecastDisplay;
import Implements.WeatherData;

public class WeatherStation {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        
        CurrentConditionDisplay ccd = new CurrentConditionDisplay(weatherData);
        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
        //第一次天气情况
        weatherData.setMeasurements(80f, 65f, 30.4f);
        System.out.println();
        //第二次天气情况
        weatherData.setMeasurements(82f, 70f, 29.2f);
        System.out.println();
        //第三次天气情况
        weatherData.setMeasurements(78f, 90f, 29.2f);
        
    }
}

运行效果
图片描述

要点:
1:观察者模式定义了对象之间一对多的关系。
2:主题用一个共同接口来更新观察者。(update方法和Subject接口)
3:观察者和可观察者(主题)之间用松耦合的方式结合,可观察者(主题)不知道观察者的细节,只知道观察者实现了观察者接口。
4:Java内置了观察者模式的实现。

自此完成了观察者模式的实例,Java在JDK当中内置主题和观察者的用法,这里为了更好理解,所以我写的是自定义的形式,
这个模式理解起来并不难,但却是个非常有用的模式,你现在就可以试着用观察者和策略模式改改以前写的旧代码,我的感受就是随着学习深入,就觉得以前写的代码很多都是有错误的。。。。。

感谢你看到这里,观察者模式部分结束,本人文笔随便,若有不足或错误之处望给予指点,90度弯腰~很快我会发布下一个设计模式内容,生命不息,编程不止!

参考书籍:《Head First 设计模式》

暖心先森
287 声望16 粉丝

生命不息,编程不止!