【Head First 设计模式】观察者模式

一、背景

本内容是来自书籍《Head First 设计模式》的第二章,观察者模式(让对象知悉你的现况)

二、OO基础

  1. 封装
  2. 继承
  3. 多态
  4. 抽象

三、OO设计原则

  1. 封装变化,将变化的部分封装起来,与固定不变的部分分开
  2. 面向接口编程,而非面向实现编程
  3. 组合优于继承
  4. 为交互对象之间的松耦合而努力

四、认识观察者模式

1.定义

观察者模式——定义了对象之间的一对多关系。这样一来,当一个对象的数据状态发生变化时,其他依赖它的对象都能感知到数据的变更

2.UML图

在这里插入图片描述

3.松耦合

观察者模式提供了一种对象设计,让主题和观察者之间松耦合(两个对象之间依然可以交互,但是互相不知道彼此的细节)。
因为主题不知道观察者的一切,它只知道观察者实现了某一个接口,主题不需要知道观察者是谁,它做了什么或其他任何细节。在不改动主题的情况,我们可以任意的增加观察者或者减少观察者,只需要让它实现某个接口就可以。主题的工作就是,当状态发生变更时,将数据通过某种协议发送给观察者。

松耦合的设计之所以能让我们建立一个有弹性的系统,是因为它使对象之间的依赖降到了最低。

五、气象站的设计

1.按照观察者模式的“推”方式实现

a.UML图

在这里插入图片描述

b.代码案例

(1)主题:

// subject
public interface Subject {
  void registerObserver(Observer observer);
  void removeObserver(Observer observer);
  void notifyObservers();
}
// concreteSubject
public class WeatherData implements Subject {
  private List<Observer> observers;
  private float temperature;
  private float humidity;
  private float pressure;

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

  @Override
  public void registerObserver(Observer observer) {
    observers.add(observer);
  }

  @Override
  public void removeObserver(Observer observer) {
    int index = observers.indexOf(observer);
    if (index >= 0){
      observers.remove(index);
    }
  }

  @Override
  public void notifyObservers() {
    for (Observer observer : observers) {
      observer.update(temperature,humidity,pressure);
    }
  }

  public void measurementsChanged(){
    notifyObservers();
  }

  public void setMeasurement(float temperature,float humidity,float pressure){
    this.temperature = temperature;
    this.humidity = humidity;
    this.pressure = pressure;
    measurementsChanged();
  }
}

(2)观察者:

public interface Observer {
  void update(float temperature,float humidity,float pressure);
}
//这里具体的观察者只展示一种,UML类图里的其他观察者实现是一样的
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);
  }

  @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");
  }
}

2.按照观察者模式的“拉”方式实现

a.UML

在这里插入图片描述

b.代码案例

“拉”的方式是使用Java内容观察者模式实现的,主题继承了java.util.Observable,观察者实现了java.util.Observer

public class WeatherDataV2 extends Observable {
  private float temperature;
  private float humidity;
  private float pressure;

  public WeatherDataV2() {

  }

  public void measurementsChanged() {
    setChanged();
    notifyObservers();
  }

  public void setMeasurements(float temp, float humidity, float pressure) {
    this.temperature = temp;
    this.humidity = humidity;
    this.pressure = pressure;
    measurementsChanged();
  }

  public float getTemperature() {
    return temperature;
  }

  public float getHumidity() {
    return humidity;
  }

  public float getPressure() {
    return pressure;
  }
}
// 其他观察者实现原理一样,不做重复粘贴了。对于DisplayElement接口和“推”一样
public class CurrentConditionsDisplay implements Observer,DisplayElement {
  Observable observable;
  private float temperature;
  private float humidity;

  public CurrentConditionsDisplay(Observable observable){
    this.observable = observable;
    observable.addObserver(this);
  }

  @Override
  public void update(Observable o, Object arg) {
    if (o instanceof WeatherDataV2){
      WeatherDataV2 weatherDataV2 = (WeatherDataV2) o;
      this.temperature = weatherDataV2.getTemperature();
      this.humidity = weatherDataV2.getHumidity();

    }
  }

  @Override
  public void display() {
    System.out.println("Current conditions: " + temperature
        + "F degrees and " + humidity + "% humidity");
  }
}

3.“推” & “拉”的优劣

(1)“推”的优点:它将全部数据都通知给了观察者,这样可以减少观察者获取数据的繁琐性
(2)“推”的缺点:当我们需要修改参数(数据)的时候,例如新增几个指标,那么这时候,我们必须修改协议,更新Observer接口中的参数个数,并且将其每个子类都要修改一遍。
(3)“拉”的优点:不必为主题新增参数或减少参数担忧,观察者主动去获取自己需要的数据。
(4)“拉”的缺点:获取数据比较繁琐
其实两者优缺点是相反的,弥补的双方的劣势,担忧不可避免的带来自己的劣势。一般来讲,人们认为“推”的方式是正确的(书上说的)

六、总结

1.观察者模式遵循了的设计原则

  1. 封装变化,将变化的部分封装起来,并且与固定不变的部分分开。 在观察者模式中,会改变的是主题的状态以及观察者的数目和类型。用这个模式,我们可以改变依赖主题的对象却不用修改主题。
  2. 面向接口编程,而非针对实现编程。 主题和观察者都使用接口,观察者使用主题的接口向主题注册,而主题使用观察者的接口向观察者通知数据,两者不必关心对方的实现细节,这样可以达到松耦合的目的。
  3. 组合优于继承。 观察者模式利用“组合”的方式将观察者注册进主题,对象之间的关系不是通过继承来实现的,而是在运行时通过组合的方式实现的。
  4. 为了交互对象之间松耦合而努力。 同2

2.观察者模式的一些要点纪要

  1. 观察者模式定义了一对多的依赖关系。
  2. 主题(也就是可观察者)用一个共同的接口来更新观察者。
  3. 观察者和可观察者之间松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。
  4. 使用此模式时,我们可从观察者处“推”或者“拉”的方式获取数据。
  5. 有多个观察者时,不可以依赖特定的通知次序。
  6. Java有多种观察者模式的实现,包括了通用的java.util.Observable,要注意java.util.Observable实现所带来的问题,因为它改变的观察者的通知顺序。如果有必要的话,我们可以实现自己的Observable,其实并不难。
  7. 许多GUI框架、javaBeans、RMI都使用了观察者模式。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值