观察者模式.
一、概念
-
观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
-
观察者模式四个角色:
1、抽象主题:定义对观察者管理的接口,包括 订阅、取消订阅、通知接口。
2、具体主题:把所有观察者对象的引用保存在一个聚集(比如ArrayList对象)里,实现抽象主题的接口,并在更新时通知观察者。
3、抽象观察者:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
4、具体观察者:实现抽象观察者定义的接口,得到主题通知的数据(pull or push),并实现自己的逻辑。 -
主题 + 观察者 = 观察者模式,可以用报纸订阅服务来模拟这个模式 —— 报纸是主题,订阅报纸的人是观察者。观察者可以选择是否订阅或者退订主题。
二、Demo实现
1、主题
java.util 包中内置了一个抽象主题 Observable 类(有弊端,下文会提到),可以选择继承 Observable 类作为具体主题角色。
@AllArgsConstructor
@Getter
public class WeatherData extends Observable {
/**
* 温度
*/
private Float temperature;
/**
* 湿度
*/
private Float humidity;
/**
* 压力
*/
private Float pressure;
/**
* 假设改变的时候这个方法会被调用
*/
public void measurementsChanged() {
setChanged();
notifyObservers(); // 这种不带参数的方式,由观察者 pull 自己想要的数据。要什么数据由观察者自己决定。
//notifyObservers(Object arg) // 这种带参数的方式,由主题 push 数据。传什么数据由主题决定。(推荐)
}
}
2、观测者
java.util 包中内置了一个抽象观察者 Observer 接口,可以选择实现 Observer 接口的 update 方法作为具体观察者角色,做更新的操作。
湿度观察者:
public class HumidityObserver implements Observer {
public HumidityObserver(Observable observable) {
// 订阅主题
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData) {
WeatherData weatherData = (WeatherData) o;
Float humidity = weatherData.getHumidity();
System.out.println("我是一个湿度观察者,我现在的湿度是:" + humidity);
}
}
}
压力观察者:
public class PressureObserver implements Observer {
public PressureObserver(Observable observable) {
// 订阅主题
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData) {
WeatherData weatherData = (WeatherData) o;
Float pressure = weatherData.getPressure();
System.out.println("我是一个压力观察者,我现在的压力是:" + pressure);
}
}
}
温度观察者:
public class TemperatureObserver implements Observer {
public TemperatureObserver(Observable observable) {
// 订阅主题
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData) {
WeatherData weatherData = (WeatherData) o;
Float temperature = weatherData.getTemperature();
System.out.println("我是一个温度观察者,我现在的温度是:" + temperature);
}
}
}
3、测试
public static void main(String[] args) {
// 具体主题实现
WeatherData weatherData = new WeatherData(24.0F,152F,100F);
// 观察者订阅
new HumidityObserver(weatherData);
new PressureObserver(weatherData);
new TemperatureObserver(weatherData);
// 事件发生
weatherData.measurementsChanged();
}
4、Observable 的弊端
- Observable 是一个“类”,而不是一个接口。所以你只能选择继承它,实现自己的具体主题,这大大限制了 Observable 的复用潜力,毕竟 Java 不支持多重继承。
- Observable 的 setChanged 方法是 protected 属性的,因此你除了选择继承 Observable 类,甚至无法使用组合来使用它。
- 如果有必要的话可以实现自己的 Observable 接口,不要害怕,这并不难。(链接代码中有示例)
tips:"抽象"主题 和 具体主题标反了。汗~
演示源代码:https://github.com/JMCuixy/design-patterns
三、总结
- 设计原则:观察者模式是为了交互对象之间的松耦合设计而努力。
- 应用场景:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
- 优点:主题是真正拥有数据的人,观察者是主题的依赖者,在数据变化时更新,这样比起让许多对象控制通一份数据类,可以得到更加干净的OO设计。
- 缺点:在应用观察者模式时需要考虑一下开发效率和运行效率的问题,因为在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,我们一般会采用异步实现。而且必须保证投递是以自恰的方式进行的。