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

原创 2016年08月30日 15:37:39

从一款天气预报 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。所有必须是子类才能调用。
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Head First Design Patterns 阅读笔记之三: Decorator Pattern

欢迎来到 Starbuzz Coffee星巴克作为一家扩展最快的咖啡店,它们现在需要更新饮料订购系统以便满足现在庞大的需求。这个订购系统的第一版如下: 显然这对于日后的维护来说,完全是个噩梦。这里需...

Head First Design Patterns 阅读笔记之四: Factory Pattern

new 的问题每当你使用 new 实例化一个具体的类时,那这就是一种实现而不是接口,而具体的实现会导致代码脆弱并且不灵活。 从技术上说,new 没有问题。问题在于需求不断变化,而这些变化又会影响 n...

Head First Design Patterns 阅读笔记之一: Strategy Pattern

本系列的所有代码参见 Head First Design Patterns—Github以 SimUDuck app 这个案例开始假设有为一家以制作鸭子池塘模拟游戏而闻名的公司,这家公司的得意之作是 ...

Head First Design Patterns 阅读笔记之六: Command Pattern

从一个远程控制系统开始假设我们要做一个远程控制系统,远端可以控制电灯、电子门的开关。远端并不需要知道具体的开关细节,用户只是按下按钮就可以控制电灯、电子门。显然,我们需要使用一种能够将方法调用封装起来...

OOP Design (Head First Design Patterns) 学习笔记--05 The factory Pattern

When you see "new", think "concrete"! 1. Identify what varies 2. Encapsulate object creation Factori...

《Head First Design Patterns》读书笔记之模板模式

因为最近在学Django,所以对于模板这个

《Head First Design Patterns》读书笔记之观察者模式

观察者模式是一种一对多的模式,yijiushi

《Head First Design Patterns》读书笔记之单例模式

/** * @author dean * 最初的单例模式设计,注意两个static */ public class SingleTest { private static SingleTest...

《Head First Design Patterns》读书笔记之迭代器模式

迭代器模式:提供一种方法顺序的访问

《Head First Design Patterns》读书笔记之命令模式

从命令这两个字我们既可以看出这个模式的应用chan
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)