观察者模式
承接上一篇策略模式。上一篇策略模式不光记录了策略模式,还简单介绍了一下OO编程的一些规则。这里会接着上一篇的内容继续添加我们在编程会遇到一些“原则”。(想具体先前的内容可以点击<策略模式[开头]>链接查看)
要点
- 观察者模式定义了对象一对多的关系
- 在此模式下,你可以接受"推送"或"被拉"操作
- 有多个观察者时,不可以依赖特定的通知次序
OO原则
- 为交互对象之间的松耦合设计而努力
下面正式进入正题,观察者模式顾名思义就是对象可以被“观察”,一旦有所改变就去通知“订阅者”。很明显观察者模式是一种一对多的关系,是一种很典型的松耦合设计。下面我们具体来看一下。
假设我们这里有个气象观测站,也就是一个WeaterData类,主要负责观察气象。然后我们有几个公告板,专门显示接收气象站的信息然后及时地更新到面板上以备工作人员能及时看到天气情况。在这种情况中,气象站是“观察者”,负责观察气象,而公告板是“订阅者”。换句话说就是一(气象站)对多(公告板)的关系。我们来看一下一般情况下我们怎么实现的
WeatherData
public class WeatherData {
private float temp;
private float humidity;
private float pressure;
public void measurementsChanged() {
temp = getTemp();
humidity = getHumidity();
pressure = getPressure();
CurrentConditionDisplay currentConditionDisplay = CurrentConditionDisplay.getInstance();
currentConditionDisplay.update(temp, humidity);
// 其他公告板实现
}
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 float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
这里的CurrentConditionDisplay是一个公告板类专门用来接收WeatherData的更新。(这里直接使用了一个单例模式,这里代码就不提出来了,关键是说明问题) 那么问题来了,如果后期有更多的公告板要加入进来,那不是每一个公告板都要有一个实例化一下。这里还有一个小细节,在公告板调用update方法时传入了两个参数,如果后期要进行更改怎么办,那不是传入的参数也需要手动去修改。这样就很明显让公告板和气象站紧耦合了。然而我们只是想让气象站有新的数据就去“通知”公告板,让公告板自己去取自己想要的数据就好,气象站可以不用知道到底有哪些公告板在“监控”自己。就好比杂志报纸订阅,我们到邮局或报刊亭去订阅一下就好,一旦有了新的期刊,我们就会收到特定的杂志或报纸,而杂志社根本就不知道到底是哪个人订阅了那么的服务。他们只要把新的期刊印刷出来发给那些订阅者就好。当我们不想订阅了,只要去通知一下就行,此后就不会在收到杂志。在编程世界里,我们也希望能做到这样,那我们应该怎么去实现呢?
好在,JDK里已经实现了一组Observable和Observer,我们只需继承实现就可以做到。下面我们具体看一下
public class WeatherData extends Observable {
private float temp;
private float humidity;
private float pressure;
public void messurementsChanged() {
setChanged(); // 有新变化后设置变化值 true 有变化 false无变换
notifyObservers(); // Observable类方法,去通知所有订阅此类的"订阅者"
}
public void setMeasurements(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
messurementsChanged(); // 设置好新变化后调动此方法通知"订阅者"
}
public float getTemp() {
return temp;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
这里需要看一下我们公告板的实现
public class CurrentConditionDisplay implements Observer {
private Observable observable;
private float temp;
private float humidity;
public CurrentConditionDisplay(Observable obs) {
this.observable = obs; // 订阅者需要知道订阅的是哪个类
observable.addObserver(this); // 将自己添加进观察者中,相当于"订阅"
}
/**
* 实现了Observer接口,重写update方法。当有新的变化时
* 就会调用此方法来更新
*
* @param o 观察者
* @param arg 变化值
*/
@Override
public void update(Observable o, Object arg) {
if(o instanceof WeatherData) {
WeatherData weatherData = (WeatherData)o;
// 从气象站获取自己想要的数据
this.temp = weatherData.getTemp();
this.humidity = weatherData.getHumidity();
}
display();
}
public void display() {
System.out.println("Current conditions: " + temp + "F degrees and " + humidity + "% humidity");
}
}
当然,订阅者可以随时取消,只要调用deleteObserver方法就可以,这里就不在具体封装取消。我们看到只实现了一个公告板,对于后期的公告板来说,只需要实现Observer接口,并添加进WeatherData里就好。这样,气象站就完全不知道到底有谁订阅了它,它只要负责获取更新数据就好然后通知各个订阅者。我们可以看一下Observable的底层代码就可以轻松知道,其实里面实现了一个vector向量,把那些订阅者都装进这个向量里,然后更新时一个个遍历调用update方法。当然,也可以自己去实现一组Observable来定制属于符合自己业务需求的观察者,只是思路是一样的。
对于setChange方法可以这么理解。当公告板想自己去“拉”数据时,需要知道气象站是否有了新的数据,如果有新数据就去获取需要的数据,否则就不操作,这样用一个bool值来随时判断是否有新的数据需要更新。
这里只是简单实现一下观察者模式,对于真正的气象站和公告板的业务肯定不会这么简单。主要是通过这个例子来说明观察者模式的松耦合设计。