《Head First 设计模式》(二):观察者模式

本文详细介绍了如何使用观察者模式设计一个气象观测站系统,WeatherData通过观察者模式更新气象统计与预报,实现布告板的动态同步。通过Subject接口、ConcreteSubject实现和Java内置Observer模式展示了松耦合的设计过程。

1. 案例Demo

建立一个气象观测站,并且这个气象观测站暂时有三种布告板,分别显示:目前的状况、气象统计、简单的预报。当 WeatherObject对象获得最新的测量数据时,三种布告板必须实时更新。

气象观测站对应 WeatherData 对象,WeatherData 对象负责追踪目前的天气状况(温度、湿度、气压)。

设计分析:

改系统分为三个部分:

  • 气象站:获取实际气象数据的物理装置
  • WeatherData 对象:追踪来自气象站的数据,并更新布告板
  • 布告板:显示目前天气状况,给用户显示

在这里插入图片描述

WeatherData 类:

在这里插入图片描述

2. 直接代码耦合的方案

直接在WeatherData 类中的 measurementsChanged()方法编写气象数据更新逻辑。

在这里插入图片描述

问题: 针对具体实现编程,后门面如果需要对布告板进行增删操作时,就必须要修改程序。

3. 使用观察者模式

观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新

设计原则:为了交互对象之间的松耦合设计而努力

图解:
在这里插入图片描述
在这里插入图片描述

3.1 观察者模式下气象站类图设计

在这里插入图片描述

3.2 手动实现观察者模式( 气象站设计 )

主题接口:

package com.jbp.designpattern.observer;

/**
 * @ClassName: Subject
 * @description: 主题接口
 * @author: JiangBeiPing
 * @create: 2021-06-24 14:17
 * @Version: 1.0
 **/
public interface Subject {

    // 注册方法,接收一个观察者类型的参数
    public void registerObserver(Observer observer);

    // 移除方法,接收一个观察者类型的参数
    public void removeObserver(Observer observer);

    // 通知方法
    public void notifyObservers();

}

主题实现类:

package com.jbp.designpattern.observer;

import java.util.ArrayList;

/**
 * @ClassName: ConcreteSubject
 * @description: 主题具体实现类
 * @author: JiangBeiPing
 * @create: 2021-06-24 14:58
 * @Version: 1.0
 **/
public class WeatherData implements Subject {

    // ArrayList 用来记录观察者,在构造器中初始化
    private ArrayList observers;

    private float temperature;

    private float humidity;

    private float pressure;

    public WeatherData() {
        observers = new ArrayList();
    }

    // 注册观察者时,只需加到ArrayList后面即可
    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    // 删除观察者,只需从ArrayList中移除
    @Override
    public void removeObserver(Observer observer) {
        int i = observers.indexOf(observer);
        if (i >= 0){
            observers.remove(i);
        }
    }

    // 此方法把主题的状态通知每个观察者
    @Override
    public void notifyObservers() {
        for (Object o : observers) {
            Observer observer = (Observer) o;
            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();
    }

}

观察者接口:

package com.jbp.designpattern.observer;

/**
 * @ClassName: Observer
 * @description: 观察者接口
 * @author: JiangBeiPing
 * @create: 2021-06-24 14:29
 * @Version: 1.0
 **/
public interface Observer {

    // 更新数据,当气象观测值改变时,主题会把这些状态值作为此方法的参数传递给观察者
    public void update(float temp,float humidity,float pressure);

}

布告板接口:

package com.jbp.designpattern.observer;

/**
 * @ClassName: DisplayElement
 * @description: 布告板接口
 * @author: JiangBeiPing
 * @create: 2021-06-24 15:29
 * @Version: 1.0
 **/
public interface DisplayElement {

    // 布告板显示数据时,调用此方法
    public void display();

}

布告板(订阅者)具体实现类:

package com.jbp.designpattern.observer;

/**
 * @ClassName: CurrentConditionsDisplay
 * @description: 布告板
 *                      Observer:实现此接口,可以从WeatherData对象获取数据改变状态
 *                      DisplayElement:布告板必须实现此接口
 * @author: JiangBeiPing
 * @create: 2021-06-25 09:55
 * @Version: 1.0
 **/
public class CurrentConditionsDisplay implements Observer,DisplayElement{

    private float temperature;

    private float humidity;

    private Subject weatherData;

    // 构造器需要weatherData对象(主题)作为注册之用
    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

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

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

    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);

        weatherData.setMeasurements(80,65,30.4f);
    }
}

3.3 使用Java内置的观察者模式( 气象站设计 )

在这里插入图片描述
用法:

对象变成观察者: 实现观察者接口(java.util.Observer),然后调用Observer对象的 addObserver()方法。取消观察者时,调用 deleteObserver()方法即可。

主题如何给观察者(订阅者)发送通知
使用Observable(java.util.Observable)类,然后:

  • 调用 setChanged()方法,标记状态已经改变的情况
  • 调用两种 notifyObservers()方法中的 notifyObservers()或 notifyObservers(Obiect arg)中的任意一个方法

观察者(订阅者)如何接收通知:
观察者实现了更新的方法:update(Observable o,Object arg)

  • Observable:把主题作为参数,方便让观察者知道是哪个主题通知的
  • Object :传入notifyObservers()的数据对象,没有则说明为空

主题如果想推(push)数据给观察者,可以把数据当为数据对象传送给notifyObservers(Obiect arg)方法。否则,观察者就必须从主题中拉(pull)数据。

WeatherData 类:

package com.jbp.designpattern.observer;

import java.util.Observable;

/**
 * @ClassName: ConcreteSubject
 * @description: 主题具体实现类
 * @author: JiangBeiPing
 * @create: 2021-06-24 14:58
 * @Version: 1.0
 **/
public class WeatherData extends Observable {

    private float temperature;

    private float humidity;

    private float pressure;

    public WeatherData() {}

    // 当从气象站得到更新观测值时,通知观察者
    public void measurementsChanged(){
        // 标记数据状态已经改变
        setChanged();
        // 使用拉方法
        notifyObservers();
    }

    // 测试方法
    public void setMeasurements(float temperature,float humidity,float pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }
}

CurrentConditionsDisplay 布告板(订阅者)具体实现类:

package com.jbp.designpattern.observer;


import java.util.Observable;
import java.util.Observer;

/**
 * @ClassName: CurrentConditionsDisplay
 * @description: 布告板
 *                      Observer:实现此接口,可以从WeatherData对象获取数据改变状态
 *                      DisplayElement:布告板必须实现此接口
 * @author: JiangBeiPing
 * @create: 2021-06-25 09:55
 * @Version: 1.0
 **/
public class CurrentConditionsDisplay implements Observer,DisplayElement{

    Observable observable;

    private float temperature;

    private float humidity;

    // 构造器需要weatherData对象(主题)作为注册之用
    public CurrentConditionsDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

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

    @Override
    public void update(Observable observable, Object arg) {
        if (observable instanceof WeatherData){
            WeatherData weatherData = (WeatherData) observable;
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            display();
        }
    }

    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);

        weatherData.setMeasurements(80,65,30.4f);
    }

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值