声明: 本文
内容属于《Head First 设计模式》阅读笔记
,文中涉及到的知识案例等直接或间接来源于该书。《Head First 设计模式》
通过有趣的图表+文字的形式,让人自然学习设计模式,非常棒
,推荐阅读
。
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
注:观察者模式是所有设计模式中应用最多、最广的设计模式,不论是MVC等框架,还是监听器、MQ等等,都有用到观察者模式。
提示: 观察者模式使用灵活,下面只是一个简单的例子,在不同的情境下,可能有不同的表现形式。
现在有一个气象站,其能实时提供变化了的气象数据;同时有三个公告板,分别要根据气象站的气象数据信息,显示:实时的气象信息、统计后的气象信息(如:统计温度平均值等等)、气象预报信息。
气象站可视为被观察者Subject,三个公告板可视为三个观察者Observer。再进行基本的抽象-封装-继承/实现-多态,可以有以下UML类图:
其中Subject为被观察者需要实现的接口,其中三个主要的方法分别是:注册(观察者开始观察被观察者)、移除(观察者取消观察被观察者)、通知(被观察者发送消息给观察者);Observer为观察者需要实现的接口,只有一个方法:更新(被观察者发送消息后,会走此方法进行各个观察者的逻辑操作)。
注:这只是最基本的方法示例,可以根据实际业务情况灵活变通或对其进行拓展。对观察者模式理念切实的理解,还需要借助一下具体实现类,下面会给出代码及测试示例,以供进一步理解此模式。
Subject:
import com.szlaozicl.designpattern.observer.observer.Observer;
/**
* 主题(即:可/被观察者)
*
* 注:所有被观察者均应实现此接口。
*
* @author JustryDeng
* @date 2019/10/12 11:53
*/
public interface Subject {
/**
* 注册(新增)观察者
*
* @param o
* 要注册的观察者对象
*
* @return 是否注册成功
* @date 2019/10/12 11:53
*/
boolean registerObserver(Observer o);
/**
* 注销(移除)观察者
*
* @param o
* 要注销的观察者对象
*
* @return 是否注销成功
* @date 2019/10/12 11:53
*/
boolean removeObserver(Observer o);
/**
* 通知观察者们
*
* @date 2019/10/12 11:53
*/
void notifyObservers();
}
WeatherData:
import com.szlaozicl.designpattern.observer.observer.Observer;
import com.szlaozicl.designpattern.observer.subject.Subject;
import lombok.Data;
import java.util.ArrayList;
/**
* 气象数据
*
* @author JustryDeng
* @date 2019/10/12 12:09
*/
@Data
public class WeatherData implements Subject {
/** 温度 */
private float temperature;
/** 湿度 */
private float humidity;
/** 气压(千帕) */
private float pressure;
/**
* 观察者容器
*
* 注: 观察者的 注册 与 注销, 操作此集合即可
*/
private ArrayList<Observer> observers = new ArrayList<>(8);
@Override
public boolean registerObserver(Observer o) {
return observers.add(o);
}
@Override
public boolean removeObserver(Observer o) {
int index = observers.indexOf(o);
if (index >= 0) {
observers.remove(index);
}
return true;
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
// 这里采用: 观察者主动从 被观察者拉数据的方式
// void update(Subject subject, Object... args);
observer.update(this);
}
}
/**
* 当参数值改变时,会调用此方法
*
* @date 2019/10/12 12:33
*/
private void measurementsChanged() {
notifyObservers();
}
/**
* 设置参数值
*
* @param temperature
* 温度
* @param humidity
* 湿度
* @param pressure
* 气压
*
* @date 2019/10/12 12:33
*/
public void setMeasurements(float temperature, float humidity, float pressure) {
boolean haveChange = this.temperature != temperature
|| this.humidity != humidity || this.pressure != pressure;
if (haveChange) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
}
Observer:
import com.szlaozicl.designpattern.observer.subject.Subject;
/**
* 观察者
*
* 注:所有观察者均应实现此接口。
*
* @author JustryDeng
* @date 2019/10/12 11:57
*/
public interface Observer {
/**
* 更新
*
* 注: 当被观察者发生变化时, 有两种方式将数据 从主题传递给观察者
* 1. 被观察者 主动传参给 观察者, 即: 被观察者主动推数据给观察者
* 2. 观察者 通过调用 被观察者的一些方法(如:getter)主动获取相关数据, 即:观察者主动从被观察者拉数据
*
* @param subject
* 主题(即:被观察者对象)
* 注:用于 观察者主动从被观察者拉数据。
* @param args
* 被观察者注定push给观察者的参数
* 注:用于 被观察者主动推数据给观察者。
*
* @date 2019/10/12 12:00
*/
void update(Subject subject, Object... args);
}
ConcreteObserver:
import com.szlaozicl.designpattern.observer.observer.Observer;
import com.szlaozicl.designpattern.observer.other.Display;
import com.szlaozicl.designpattern.observer.subject.Subject;
import com.szlaozicl.designpattern.observer.subject.impl.WeatherData;
import lombok.Data;
/**
* 实时信息 观察者
*
* @author JustryDeng
* @date 2019/10/12 12:48
*/
@Data
public class ConcreteObserver implements Observer, Display {
/** 温度 */
private float temperature;
/** 湿度 */
private float humidity;
/** 气压(千帕) */
private float pressure;
@Override
public void update(Subject subject, Object... args) {
if (subject instanceof WeatherData) {
WeatherData weatherData = (WeatherData)subject;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
this.pressure = weatherData.getPressure();
}
display();
}
@Override
public void display() {
String info = "\n【实时信息观察者ConcreteObserver】观察到的数据是:\n"
.concat("\t当前温度: ").concat(String.valueOf(temperature)).concat("\n")
.concat("\t当前湿度: ").concat(String.valueOf(humidity)).concat("\n")
.concat("\t当前气压: ").concat(String.valueOf(pressure)).concat("\n");
System.out.println(info);
}
}
注:观察者ForecastObserver与观察者StatisticsObserver的代码,这里就不再给出了,可详见文末完整代码托管链接。
import com.szlaozicl.designpattern.observer.observer.impl.ConcreteObserver;
import com.szlaozicl.designpattern.observer.observer.impl.ForecastObserver;
import com.szlaozicl.designpattern.observer.observer.impl.StatisticsObserver;
import com.szlaozicl.designpattern.observer.subject.impl.WeatherData;
import java.util.concurrent.TimeUnit;
/**
* 测试 --- 观察者模式
*
* @author JustryDeng
* @date 2019/10/12 13:23
*/
public class Test {
/**
* 函数入口
*/
public static void main(String[] args) throws InterruptedException {
/// 创建 被观察者
WeatherData weatherData = new WeatherData();
/// 创建 观察者们
ConcreteObserver concreteObserver = new ConcreteObserver();
ForecastObserver forecastObserver = new ForecastObserver();
StatisticsObserver statisticsObserver = new StatisticsObserver();
/// 观察者 进行注册,开始观察 被观察者
boolean resultOne = weatherData.registerObserver(concreteObserver);
boolean resultTwo = weatherData.registerObserver(forecastObserver);
boolean resultThree = weatherData.registerObserver(statisticsObserver);
System.out.println("concreteObserver注册观察weatherData, " + (resultOne ? "成功!" : "失败!"));
System.out.println("forecastObserver注册观察weatherData, " + (resultTwo ? "成功!" : "失败!"));
System.out.println("registerObserver注册观察weatherData, " + (resultThree ? "成功!" : "失败!"));
/// ------------------------ 测试验证
// 被观察者 第一次数据发生变化
TimeUnit.MILLISECONDS.sleep(100);
System.err.println("\n被观察者 第一次 数据发生变化!");
weatherData.setMeasurements(10.5F, 0.23F, 50.2F);
// 被观察者 第二次数据发生变化
TimeUnit.MILLISECONDS.sleep(100);
System.err.println("被观察者 第二次 数据发生变化!");
weatherData.setMeasurements(27.5F, 0.68F, 100.2F);
/// 观察者 取消观察
boolean resultFour = weatherData.removeObserver(concreteObserver);
boolean resultFive = weatherData.removeObserver(forecastObserver);
boolean resultSix = weatherData.removeObserver(statisticsObserver);
System.out.println("concreteObserver取消观察weatherData, " + (resultFour ? "成功!" : "失败!"));
System.out.println("forecastObserver取消观察weatherData, " + (resultFive ? "成功!" : "失败!"));
System.out.println("registerObserver取消观察weatherData, " + (resultSix ? "成功!" : "失败!"));
// 被观察者 第三次数据发生变化
TimeUnit.MILLISECONDS.sleep(100);
System.err.println("\n被观察者 第三次 数据发生变化!");
weatherData.setMeasurements(30.5F, 0.88F, 680.2F);
}
}
观察者模式学习完毕 !
^_^ 如有不当之处,欢迎指正
^_^ 参考资料
《Head First 设计模式》Eric Freeman & Elisabeth Freeman with Kathy Sierra & Bert Bates著,O’Reilly Taiwan公司译,UMLChina改编
^_^ 测试代码托管链接
https://github.com/JustryDeng…DesignPattern
^_^ 本文已经被收录进《程序员成长笔记(六)》,笔者JustryDeng