观察者模式 - Head First设计模式学习(一)

本文介绍如何使用观察者模式实现气象站数据更新布告板。通过解耦气象站与布告板,实现数据更新时自动通知并更新多个布告板,避免代码冗余,提高系统灵活性。

有这么一个需求。

  1. 气象站会发布气象监测数据,包括(温度,湿度,气压)
  2. 需要开发一个应用,从气象站取得数据,并更新应用的布告板。
  3. 布告板有三类,分别是显示当前天气情况,气象统计数据,天气预报。

气象站提供的数据结构如下,很简单,就是温度,湿度,气压的获取,以及气象数据变化时触发的方法。


public class WeatherData {
	
	private float temperature; 
	
	private float humidity;
	
	private float pressure;

	public float getTemperature() {
		return temperature;
	}

	public float getHumidity() {
		return humidity;
	}

	public float getPressure() {
		return pressure;
	}

    //气象数据发生改变的时候执行的方法
	public void measurementsChanged() {
		
	}
	
	
}

按照通常面向过程的编程方式,会是在 measurementChanged 方法当中添加布告板的显示更新逻辑。

public class WeatherData {
	
	private float temperature; 
	
	private float humidity;
	
	private float pressure;

	public float getTemperature() {
		return temperature;
	}

	public float getHumidity() {
		return humidity;
	}

	public float getPressure() {
		return pressure;
	}

	CurrentConditionDisplay currentConditionDisplay = new CurrentConditionDisplay();  //当前天气情况显示
	
	ForecastDisplay forecastDisplay = new ForecastDisplay();  //天气预报显示
	
	StatisticsDisplay statisticsDisplay = new StatisticsDisplay();	 //气象统计显示
	
	public void measurementsChanged() {
		currentConditionDisplay.update(temperature, humidity, pressure);
		forecastDisplay.update(temperature, humidity, pressure);
		statisticsDisplay.update(temperature, humidity, pressure);
	}
	
}

这样的编程方式导致每次要添加布告板的实现都要来修改 WeatherData 的代码。

引入观察者模式去解决这个问题。
什么是观察者模式,可以通过报纸和杂志的订阅来解释。

  1. 报社的任务就是出版报纸
  2. 向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,你就会一直收到新报纸。
  3. 当你不想看报纸的时候,取消订阅,他们就不会给你送新报纸来。
  4. 只要报社还在运营,就会有人向他们订阅报纸或者曲线订阅

观察者模式将出版者称为 “主题(Subject)”,订阅者称为“观察者(Observer)”

同订阅报纸的模式。

  1. 观察者可以订阅主题
  2. 主题的信息发生改变时,会通知观察者,把数据传递给观察者。
  3. 观察者不想接受主题的信息的时候就可以取消主题。

观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖都会收到通知并且自动更新。

观察者类图如下:

在这里插入图片描述

观察者模式提供了一种对象设计,让主题和观察者之间松耦合。当两个对象之间松耦合,他们依然可以交互,但是不太清楚彼此的细节。

改变主题或者观察者的其中一方,并不会影响另一方。因为两者时松耦合的,所以只要他们直接的接口仍被遵守,我们就可以自由的改变他们。

松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。

根据观察者模式,重写气象布告板的程序。
类图设计如下:
在这里插入图片描述

代码程序如下:

接口的设计

//主题定义
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();
	
}

气象站的数据源程序

public class WeatherData implements Subject{
	
	
	private ArrayList<Observer> observers;
	
	private float temperature; 
	
	private float humidity;
	
	private float pressure;

	public float getTemperature() {
		return temperature;
	}

	public float getHumidity() {
		return humidity;
	}

	public float getPressure() {
		return pressure;
	}

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

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

	@Override
	public void removeObserver(Observer o) {
		observers.remove(o);
	}

	@Override
	public void notifyObserver() {
		float temp = this.getTemperature();
		float humidity = this.getHumidity();
		float pressure = this.getPressure();
		for (Observer o : observers) {
			o.update(temp, humidity, pressure);
		}
	}
	
	public void measurementsChanged() {
		this.notifyObserver();
	}
	
	public void setMeasurements(float temp, float humidity, float pressure) {
		this.temperature = temp;
		this.humidity = humidity;
		this.pressure = pressure;
		this.measurementsChanged();
	}
}

布告板的实现


public class CurrentConditionDisplay implements Observer, DisplayElement {
	
	private float temperature;
	
	private float humidity;
	
	private float pressure;
	
	private Subject subject;
	
	public CurrentConditionDisplay(Subject weatherData) {
		this.subject = weatherData;
		weatherData.registerObserver(this);
	}
	
	@Override
	public void update(float temperature, float humidity, float pressure) {
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		this.display();
	}

	@Override
	public void display() {
		System.out.println("this is  currentConditionDisplay. temp: "+this.temperature+", humidity: "+this.humidity+", pressure:"+this.pressure);
	}
	
}

测试程序


public class Main {
	
	public static void main(String[] args) {
		WeatherData s = new WeatherData();
		Observer o = new CurrentConditionDisplay(s);
		s.setMeasurements(1, 2, 3);
		s.setMeasurements(4, 5, 6);
		
	}
	
}


测试结果:
this is  currentConditionDisplay. temp: 1.0, humidity: 2.0, pressure:3.0
this is  currentConditionDisplay. temp: 4.0, humidity: 5.0, pressure:6.0

由于观察者模式的普遍,各类语言或者框架都提供了观察者模式的实现。
例如 java 提供了 Observable 和 Observer 两个超类和接口。分别对应于前文提及的主题和观察者。

使用 Observable 和 Observer 实现布告板程序

气象站数据源程序,继承于 Observable 超类

package observer.example3;

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

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

	public float getTemperature() {
		return temperature;
	}

	public float getHumidity() {
		return humidity;
	}

	public float getPressure() {
		return pressure;
	}

	public WeatherData() {
		
	}
	

	public void measurementsChanged() {
		this.setChanged(); //标识主题改变
		this.notifyObservers(this); //通知所有的观察者
	}
	
	public void setMeasurements(float temp, float humidity, float pressure) {
		this.temperature = temp;
		this.humidity = humidity;
		this.pressure = pressure;
		this.measurementsChanged();
	}
}

布告板程序,实现 Observer 接口,必须实现 update 方法

package observer.example3;

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

public class CurrentConditionDisplay implements Observer, DisplayElement {
	
	private float temperature;
	
	private float humidity;
	
	private float pressure;
	
	private Observable subject;
	
	public CurrentConditionDisplay(Observable weatherData) {
		this.subject = weatherData;
		weatherData.addObserver(this);
	}
	
	@Override
	public void display() {
		System.out.println("this is  currentConditionDisplay. temp: "+this.temperature+", humidity: "+this.humidity+", pressure:"+this.pressure);
	}

	//响应主题的推送
	@Override
	public void update(Observable o, Object arg) {
		WeatherData w = (WeatherData)o;
		this.temperature = w.getTemperature();
		this.humidity = w.getHumidity();
		this.pressure = w.getPressure();
		this.display();
	}
	
}

测试程序

public class Main {
	
	public static void main(String[] args) {
		WeatherData s = new WeatherData();
		Observer o = new CurrentConditionDisplay(s);
		s.setMeasurements(1, 2, 3);
		s.setMeasurements(4, 5, 6);
		
	}
	
}


测试程序
this is  currentConditionDisplay. temp: 1.0, humidity: 2.0, pressure:3.0
this is  currentConditionDisplay. temp: 4.0, humidity: 5.0, pressure:6.0

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值