观察者模式
定义
设计模式的一种。在此种模式中,一个目标对象
管理所有
相依于它的观察者对象,并且在它本身的状态改变时
主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统。( 引用维基百科 )
观察者模式(Observer)完美的将观察者和被观察的对象分离开。举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。面向对象设计的一个原则是:
系统中的每个类将重点放在某一个功能上,而不是其他方面。
一个对象只做一件事情,并且将他做好。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。
( 引用百度百科 )
白话文
出版者+订阅者=观察者模式
。主题对象管理某些数据,当主题内的数据改变,就会通知观察者。观察者已经订阅(注册)主题以便在主题数据改变时能够收到更新。
类图
角色解析
-
Subject
观察对象。定义了注册、删除、通知观察者接口。
-
ConcreteSubject
观察对象具体实现类。
-
Observer
所有潜在的观察者必须实现观察者接口。当主题状态改时它被调用。
-
ConcreteObserver
具体的观察者可以是实现此接口的任意类。观察者必须注册到具体主题,以便接收更新。
实现
1. 定义主题接口
/**
* 主题接口
* 注册、通知、移除
*/
public interface Subject {
/**
* 注册观察者
* @param o
*/
void registerObserver(Observer o);
/**
* 移除观察者
* @param o
*/
void removeObserver(Observer o);
/**
* 当主题状态改变时,这个方法会被调用,以通知所有的观察者
*/
void notifyObservers();
}
2. 主题接口实现类
/**
* 主题接口实现类
*/
public class WeatherData implements Subject{
/**
* 温度
*/
private float temperature;
/**
* 湿度
*/
private float humidity;
/**
* 压力
*/
private float pressure;
/**
* 观察者列表
*/
private List<Observer> observerList;
public WeatherData() {
observerList = new ArrayList<>();
}
/**
* 注册观察者对象
* @param o
*/
@Override
public void registerObserver(Observer o) {
observerList.add(o);
}
/**
* 解除观察者对象
* @param o
*/
@Override
public void removeObserver(Observer o) {
observerList.remove(o);
}
/**
* 通知所有观察者
*/
@Override
public void notifyObservers() {
for (Observer observer : observerList) {
observer.update(temperature, humidity, pressure);
}
}
/**
* 当测试值改变时,调用通知方法
*/
public void measurementsChange() {
notifyObservers();
}
/**
* 此方法模拟测量值改变
*/
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChange();
}
}
3. 观察者接口
/**
* 定义观察者共同行为
*/
public interface Observer {
/**
* 所有观察者必须实现接口。当主题值改变时,会调用此方法
* @param temp
* @param humidity
* @param pressure
*/
void update(float temp, float humidity, float pressure);
}
4. 观察者具体实现类
/**
* 当前观察者实现类之一
*/
public class CurrentConditionsDisplay implements Observer, DisplayElement{
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData) {
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("Current Condition: " + temperature + " F degrees and " + humidity + "% humidity.");
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
}
/**
* 当前观察者实现类之二
*/
public class TempConditionDisplay implements Observer, DisplayElement{
private float temp;
public TempConditionDisplay(Subject subject) {
subject.registerObserver(this);
}
@Override
public void display() {
System.out.println("Temp Condition Display,Current temp is " + temp);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temp = temp;
display();
}
}
5. 其他接口及实现类
/**
* 布告板显示接口
*/
public interface DisplayElement {
void display();
}
6. Main方法
public class M {
public static void main(String[] args) {
// 创建主题类
WeatherData weatherData = new WeatherData();
// 创建观察者并把具体主题引入传入
// 观察者之一
CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
// 观察者之二
TempConditionDisplay tempConditionDisplay = new TempConditionDisplay(weatherData);
// 温度变化
weatherData.setMeasurements(1F, 3F, 4F);
}
}
总结
- 观察者模式提供了一种设计对象,让主题和观察者之间松耦合。主题只知道观察者实现了某个接口,并不需要知道观察者的具体类是谁、做了些什么或其他任何细节。
- 任何时候我们都可以增加新的观察者,因为主题唯一依赖的东西是一个实现Observer接口的对象列表。可以动态新增、删除、替换观察者,而其他观察者不受影响。只要它们之间的接口仍被遵守,我们就可以自由改变它们。松耦合的设计能让我们建立弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。
- 定义对象间的一种一对多的依赖关系。当一个对象状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
- 观察者模式可以实现
拉
和推
两种动作。 - 注意: 不要依赖于观察者被通知的次序。
- Java有内置的观察者模式。包含最基本的Observer接口与Observable。但是Observable是一个
类
而非接口,限制了它的使用和复用。自Java1.9废弃。
* This class and the {@link Observer} interface have been deprecated.
* The event model supported by {@code Observer} and {@code Observable}
* is quite limited, the order of notifications delivered by
* {@code Observable} is unspecified, and state changes are not in
* one-for-one correspondence with notifications.
* For a richer event model, consider using the
* {@link java.beans} package. For reliable and ordered
* messaging among threads, consider using one of the concurrent data
* structures in the {@link java.util.concurrent} package.
* For reactive streams style programming, see the
* {@link java.util.concurrent.Flow} API.
- 如果一下被观察者对象有很多直接和间接的观察者的话,将所有的观察者都通知会花费很多时间。
- 如果在观察者和观察目标之间有循环依赖的话,会触发它们之间的循环引用,进而导致系统崩溃。