1.前言
本期分享设计模式主题:观察者设计模式。
这是一个非常简单易学的设计模式,读完本文,你能知道观察者模式的设计以及设计思想。
2.模式简介
这是一个可以帮助你的对象知悉现状,不会错过对该对象感兴趣的事。对象甚至在运行时可决定是否需要继续被通知。观察者模式也是JDK中使用最多的模式之一,非常有用。
3.模式定义
观察者模式是在对象之间,定义一对多的依赖关系。基于这样的关系,如果对象发生状态变化,与之依赖的多个对象(监听的对象)能够收到状态变化通知,并进行更新。是一个以松耦合的方式,让一个对象与一系列对象进行协作沟通的方式。
观察者模式有两种方式的实现,一种是推(push),另一种是主动拉(pull),推和拉都是针对通知而言。本着介绍推模式。
4.模式UML类图
观察者模式的UML类图如下图:
角色划分
- Subject:主题或中心对象,一对多中的一,接口或抽象类。定义了如何添加观察者,移除观察者,通知观察者更新的基本行为。
- SubjectImpl1和SujectImpl2:是对主题的具体实现,可以通过实现Subject的接口实现不同种类的主题和中心对象。
- Observer:观察者接口定义。定义观察者更新行为。
- ObserverImpl1和ObserverImpl2:观察者的实现类,可以通过实现Observer接口实现不同的观察者,监听不同的主题。
5.代码实例
实例介绍
实例实现了一个天气中心,向一个当前天气显示器上通知最新的气象数据:温度、湿度、压强。气象显示器会更新显示的数据,并实时显示。
主题中心类:
/**
* 主题类
* @author Misout
* @date 2018-03-25 12:40:37
*/
public interface Subject {
/**
* 注册添加观察者
* @param observer
*/
void registerObserver(Observer observer);
/**
* 移除观察者
* @param observer
*/
void removeObserver(Observer observer);
/**
* 更新或通知变更所有观察者
*/
void notifyObservers();
}
具体主题实现类:
/**
* 具体主题实现类:一个天气数据中心
* 一旦更新数据,实时通知观察者
* @author Misout
* @date 2018-03-25 12:51:19
*/
public class WeatherData implements Subject {
/** 已注册的观察者列表 */
private List<Observer> observers;
/** 温度 */
private float temp;
/** 湿度 */
private float humidity;
/** 压强 */
private float pressure;
public WeatherData() {
observers = new ArrayList<Observer>();
}
@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(temp, humidity, pressure);
}
}
/**
* 发生数据变化时,实时通知观察者
*/
public void measurementsChanged() {
notifyObservers();
}
public void setMeasurements(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public float getTemp() {
return temp;
}
public void setTemp(float temp) {
this.temp = temp;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
}
观察者接口定义:
/**
* 观察者
* @author Misout
* @date 2018-03-25 12:40:28
*/
public interface Observer {
/**
* 更新温度、湿度、压强.
* 缺点:将更新的参数值直接放入型参,如果未来要增减观察指标,将不可扩展。
* @param temp 温度
* @param humidity 湿度
* @param pressure 压强
*/
void update(float temp, float humidity, float pressure);
}
观察者具体实现类:
/**
* @author Misout
* @date 2018-03-25 13:33:58
*/
public class CurrentConditionsDisplay
implements Observer{
private float temp;
private float humidity;
private float pressure;
private Subject weatherData;
/**
* 注册到指定的Subject,进行监听
* @param weatherData
*/
public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity,
float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
display();
}
public void display() {
System.out.println("Current conditions: "
+ temp + "F degrees and " + humidity
+ "% humidity and " + pressure
+ " pressure");
}
}
测试代码:
/**
* @author Misout
* @date 2018-03-25 13:37:49
*/
public class WeatherStationTest {
public static void main(String[] args) {
// 创建一个主题实现类:天气数据中心
WeatherData weatherData = new WeatherData();
// 创建一个观察者:当前天气显示器,并注册到天气中心
CurrentConditionsDisplay currentDisplay =
new CurrentConditionsDisplay(weatherData);
// 天气中心变更数据,自动通知到观察者
weatherData.setMeasurements(37, 50, 39.4f);
}
}
输出:
Current conditions: 37.0F degrees and 50.0% humidity and 39.4 pressure
6.JDK中的观察者模式
在JDK中已经提供了一套观察者模式的实现:Observable类相当于这里的Subject,Observer接口相当于这里的Observer接口
类图关系如下:
值得注意的是,JDK中的主题类叫作Observable,是一个类,并非一个接口。这是一个缺陷,因为要想通过JDK的API实现观察者模式,必须继承Observable类,并不满足面对接口编程的设计原则,如果某个类想具有Observable和其他另一个父类的功能,就陷入两难的境地。因为Java类并不支持多继承。
7.总结
1、封装变化的部分。
2、多用组合、少用继承:JDK中的观察者模式给了一个教训。
3、针对接口编程,不针对实现编程。
4、为对象之间的依赖关系达到松耦合设计而努力。
推荐阅读:
设计模式(一)策略模式