设计模式——观察者模式

文章目录

  • [1 概述](#1 概述)
  • [2 实现](#2 实现)
  • [3 总结](#3 总结)

1 概述

观察者模式可以分为观察者和被观察者,观察者通过注册到一个被观察者中,也可视为订阅,当被观察者的数据发生改变时,会通知到观察者,观察者可以据此做出反应。

可以类比订阅报纸,报社就是被观察者,订阅者就是观察者,订阅者通过订阅报纸与报社建立联系,而报社有新报纸则主动投递给订阅者。

2 实现

这里以Head First 设计模式中的观察者模型为例。

讲的是一个气象监测站模型,气象站有三个传感器,分别采集温度、湿度和气压三个值。气象站采集完数据之后会将数据设置到WeatherData对象中,而WeatherData数据更新后需要同时将数据更新到三个显示装置中。

这里就是使用了观察者模式,WeatherData是数据中心,是被观察者,而显示装置则是观察者,当观察者订阅之后,数据中心的变化都会主动通知到观察者。

这个模型的类图如下:

其中最重要的就是Subject和Observer接口,这里Subject就是被观察者的总接口,而Observer接口则是观察者总接口。Display接口则是因为多个显示器都拥有共同的Display行为。

WeatherData实现Subject,成为一个具体的Subject,而三个Display则实现Observer接口,成为观察者实例。

下面是代码实例:

Subject接口

java 复制代码
public interface Subject {
    void registerObserver(Observer o);

    void removeObserver(Observer o);

    void notifyObservers();
}

拥有对Observer对象操作的注册和删除操作,也有通知各个Observer的方法。

Observer接口

java 复制代码
public interface Observer {
    void update(float temperature, float humidity, float pressure);
}

观察者接口,拥有Observer的公用操作,Subject通过该接口来更新各个Observer中的数据。被观察者其实就是通过这个接口来通知到各个观察者的

DisplayElement接口

java 复制代码
public interface DisplayElement {
	public void display();
}

各个Display的公用方法

WeatherData类

java 复制代码
public class WeatherData implements Subject {
	private List<Observer> observers;
	private float temperature;
	private float humidity;
	private float pressure;
	
	public WeatherData() {
		observers = new ArrayList<Observer>();
	}
	
	public void registerObserver(Observer o) {
		observers.add(o);
	}
	
	public void removeObserver(Observer o) {
		observers.remove(o);
	}
	
	public void notifyObservers() {
		for (Observer observer : observers) {
			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();
	}

	public float getTemperature() {
		return temperature;
	}
	
	public float getHumidity() {
		return humidity;
	}
	
	public float getPressure() {
		return pressure;
	}

}

具体的被观察者类,会有一个存有所有观察者对象的集合,当数据变化时,会遍历这个集合来通知观察者,而通知就是调用观察者的update方法。

ForecastDisplay类

java 复制代码
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);
	}

	public void update(float temp, float humidity, float pressure) {
        lastPressure = currentPressure;
		currentPressure = pressure;

		display();
	}

	public void display() {
		System.out.print("Forecast: ");
		if (currentPressure > lastPressure) {
			System.out.println("Improving weather on the way!");
		} else if (currentPressure == lastPressure) {
			System.out.println("More of the same");
		} else if (currentPressure < lastPressure) {
			System.out.println("Watch out for cooler, rainy weather");
		}
	}
}

这是三个具体的观察者其中的一个,实现了Observer接口,并持有WeatherData对象,在ForecastDisplay对象创建的时候,会将自己加到WeatherData被观察者对象的集合中保存。当数据变化时,WeatherData会从集合中遍历到这个对象,并调用其update方法。

其他三个观察者类似。

测试代码:

java 复制代码
public class WeatherStation {

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

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

三个观察者显示器获取到了同样的更新数据,但是他们可以根据自身的显示逻辑来做出不同的显示结果

java 复制代码
Current conditions: 80.0F degrees and 65.0% humidity
Avg/Max/Min temperature = 80.0/80.0/80.0
Forecast: Improving weather on the way!
Current conditions: 82.0F degrees and 70.0% humidity
Avg/Max/Min temperature = 81.0/82.0/80.0
Forecast: Watch out for cooler, rainy weather
Current conditions: 78.0F degrees and 90.0% humidity
Avg/Max/Min temperature = 80.0/82.0/78.0
Forecast: More of the same
Current conditions: 62.0F degrees and 90.0% humidity
Avg/Max/Min temperature = 75.5/82.0/62.0

Process finished with exit code 0

3 总结

  1. 观察者加被观察者组成观察者模式
  2. 观察者继承Observer接口,该接口提供一个通知观察者的方法
  3. 观察者持有被观察者的引用,在构造方法中调用被观察者的注册方法将自身注册为一个观察者
  4. 被观察者拥有一个观察者集合,用于存储所有注册的观察者的对象
  5. 观察者可以自己调用注册和注销方法将自身添加到被观察者的列表中或从列表中移除
  6. 被观察者要通知观察者时,遍历观察者集合,调用观察者接口中的方法通知观察者
相关推荐
有意义1 小时前
Spring Boot 项目中部门查询功能实现与依赖注入优化
后端·设计模式
岁忧4 小时前
Go channel 的核心概念、操作语义、设计模式和实践要点
网络·设计模式·golang
songgeb6 小时前
《设计模式之美》之适配器模式
设计模式
Yeniden6 小时前
【设计模式】享元模式(Flyweight)大白话讲解!
java·设计模式·享元模式
乙己4076 小时前
设计模式——单例模式(singleton)
java·c++·单例模式·设计模式
这不小天嘛7 小时前
23 种经典设计模式的名称、意图及适用场景概述
设计模式
数据知道18 小时前
Go语言设计模式:适配器模式详解
设计模式·golang·建造者模式
执笔论英雄18 小时前
【设计模式】策略类和依赖注入
设计模式
手把手入门1 天前
23种设计模式
设计模式
qqxhb1 天前
系统架构设计师备考第59天——SOA原则&设计模式
设计模式·系统架构·版本管理·标准化·松耦合·可复用·服务粒度