Head First Design Patterns 阅读笔记之二: Observer Pattern

本文通过分析一款天气预报APP的开发需求,引入观察者模式,讲解其核心概念与应用。通过对比不同设计方案,强调了观察者模式带来的松耦合优势,并展示了如何在Java中利用内置的Observable和Observer接口实现该模式。文章最后讨论了使用内置API的限制,鼓励读者深入理解设计模式以提升代码可维护性。
摘要由CSDN通过智能技术生成

从一款天气预报 APP 开始

假设现在需要编写一款 APP,可以显示感应器传来的当前天气条件(温度、湿度、气压),报告当前的天气状况统计数据并做简单的预测。现在已知以下条件:

  • WeatherData 类有三个 getter 方法来获取当前的数据:getTemperature()、getHumidity()、getPressure()。
  • WeatherData 类中有一个方法 measurementsChanged() ,每当新的天气数据获得的时候就被调用。
  • 我们需要利用当前的数据显示上面提到的三个方面,并且在数据更新时需要更新显示内容。
  • 系统必须易扩展,其他开发者可以添加新的显示项。

初稿完成?

根据要求写下如下代码:

public class WeatherData 
{
    // instance variable declarations 
    public void measurementsChanged() 
    {
        float temp = getTemperature(); 
        float humidity = getHumidity(); 
        float pressure = getPressure();

        currentConditionsDisplay.update(temp, humidity, pressure); 
        statisticsDisplay.update(temp, humidity, pressure); 
        forecastDisplay.update(temp, humidity, pressure);
    }
    // other WeatherData methods here 
}

这样看上去对,但是存在很多问题:
- 由于 currentConditionDisplay 几个类都是具体实现的,所以动态添加其他显示选项,完全不可能。
- temp、humidity、pressure 这些会变化的地方最好使用接口,由设计原则可知,我们需要封装会变化的地方。


观察者模式

首先看一下杂志订阅的过程:

  • 杂志出版商开始出版杂志。
  • 你订阅一家杂志,每当它退出新刊物,就会送给你。只要你保持订阅,就始终会获得新的杂志。
  • 一旦你停止订阅,就不会再给你送杂志。
  • 只要杂志出版商还存在,就会有人订阅或取消订阅。

观察者模式可以用如下公式定义:

Publishers + Subscribers = Observer Pattern

在观察者模式中,我们把出版商叫做 Subject,订阅者叫做Observer。正式定义如下:

The Observer Pattern defines a one-to-many dependency between objects so that when one objet changes state, all of its dependents are notified and updated automatically.

在后面,你会发现有不同的方式实现观察者模式,但是大多数都以设计包含 Subject 和 Observer 接口的类为核心。一般的设计如下图:
The Observer Pattern


松耦合的好处

如果两个对象是松耦合的,它们互相交流但是彼此了解很少。观察者模式使得 subject 和 observer 是松耦合的。为什么?

  • subject 只知道 observer 实现了某一个接口,所以我们可以在任意时刻增加或删除新的 observer。
  • 增加新的 observer 类型时,我们不需要修改 subject。
  • 我们可以独立的重用 subject 和 observer,它们当中一个的改变并不会影响另一个。

这里需要提到一个新的设计原则

在有联系的对象之间追求松耦合。
Strive for loosely coupled designs between objects that interact.


修改后的版本

画出相应的设计图:
Diagram

// 几个接口
public interface Subject
{
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObserver();
}

public interface Observer
{
    public void update(float temp, float humidity, float pressure);
}

public interface DisplayElement
{
    public void display();
}
// 实现 Subject
import java.util.ArrayList;

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 removeObserver(Observer o) 
    { 
        int i = observers.indexOf(o);
        if (i >= 0) 
        {
            observers.remove(i); 
        }
    }

    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(); 
    }
    // other WeatherData methods here
}
// 实现 Observer
public class CurrentConditionsDisplay implements Observer, DisplayElement 
{ 
    private float temperature;
    private float humidity;
    private Subject weatherData;

    public CurrentConditionsDisplay(Subject weatherData) 
    { 
        this.weatherData = weatherData; 
        weatherData.registerObserver(this);
    }

    public void update(float temperature, float humidity, float pressure) 
    { 
        this.temperature = temperature;
        this.humidity = humidity;
        display();
    }

    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 = new WeatherData();
        CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);

        weatherData.setMeasurements(80, 65, 30.4f); 
        weatherData.setMeasurements(82, 70, 29.2f); 
        weatherData.setMeasurements(78, 90, 29.2f);
    } 
}

一般信息都是 observer 用 pull 的方式获取的,当然也可以使用 push 方式(内置的观察者模式可以实现),但是我们一般认为 pull 方式更好。


利用 Java 内置的观察者模式

Java API 中拥有内置的观察者模式:java.util 包中的 Observable 类和 Observer 接口。它们分别对应于前面的 Subject 和 Observer 接口。使用情况如下:
Observer built-in

现在利用内置的 API 重写程序:
part1

part2

但是直接使用内置的 API 存在问题:

  • observable 是一个类,而不是接口
  • observable 中一些关键方法的访问修饰符是 protected。所有必须是子类才能调用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值