设计模式
定义
观察者模式定义
在对象之间定义一对多依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知并自动更新。
OO原则
- 封装变化
- 多用组合,少用继承
- 针对接口编程,不针对实现编程
- 为交互对象之间的松耦设计而努力
气候站实例
描述
观察站通过硬件获取气候条件数据(Temp, Humidity and Pressure),然后自己设计各种看板实时展示这些数据。要求当观察站检测到的气候数据变化后,将最新的数据通知给各个看板,而后看板自动更新展示详情。
设计
在这个实例中我们使用观察者模式来设计,首先,我们可以分辨出气候站对象是主题(subject/observable),而各种看板则是观察者(observer)。Observable需要知道所有跟随他的Observer的信息,并以此通知他们,所以Observer需要能够向Obervable注册自己,并且也需要能够随时脱离。 而后我们设计两种角色需要设计到的功能: Observable
- 添加observer
- 删除observer
- 通知observers
Observer
- 更新自身状态
根据OO原则我们开始设计接口类:
public interface Observable {
public void addObserver(Observer observer);
public void deleteObserver(Observer observer);
public void notifyObservers();
}
public interface Observer {
public void update();
}
接口类设计好后,我们可以开始针对接口进行具体的实现了。在观察者模式里我们需要构建一对多关系,也就是说Observable和Observers之间的关系是一对多关系。因此我们这里提出了一种新的OO原则
为交互对象之间的松耦设计而努力
因此在实现具体的Observable的时候,我们间关系的构建映射到管理Observers接口类,就可以将具体的Observable实现与Observers之间解耦。我们在需要修改或者新增各种不同的Observers的时候,就不需要改动Observable的相关代码了。
具体对象的实现
WeatherData(Observable具体实现)
package WeatherApplication.Subject;
import WeatherApplication.Observer.Observer;
import java.util.ArrayList;
public class WeatherData implements Subject {
private ArrayList observers;
private float temp;
private float humidity;
private float pressure;
public WeatherData() {
this.observers = new ArrayList();
}
@Override
public void registerObserver(Observer observer) {
this.observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
int index = this.observers.indexOf(observer);
if (index >=0 ){
this.observers.remove(index);
}
}
@Override
public void notifyObservers() {
for (int i = 0; i < this.observers.size(); i++) {
Observer observer = (Observer) this.observers.get(i);
observer.update(this.temp,this.humidity,this.pressure);
}
}
public void measurementChanged() {
this.notifyObservers();
}
public void setMeasurements(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
this.measurementChanged();
}
}
CurrentConditionDisplay(看板的一种实现)
package WeatherApplication.Observer;
import WeatherApplication.Subject.Subject;
public class CurrentConditionsDisplay implements Observer, DisplayElement{
private float temp;
private float humidity;
private Subject mySubject;
public CurrentConditionsDisplay(Subject subject) {
this.mySubject = subject;
this.mySubject.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.display();
}
@Override
public void display() {
System.out.println("Current Conditions: " + this.temp + " " + this.humidity);
}
}
进一步设计
上述实现当中,我们可以看到,在定义update方法的时候,我们需要指定传输进去的变量,那么存在一个问题,为了保证接口的统一,我们会将Observers可能不需要的数据也一并传给他。那么Observers就像,能否他们需要什么数据自己去向Observable要呢?这样Observable只需要告知Observers数据更新这个事件的发生,而后Observers自己通过Observable提供的getter方法去获取自己想要的数据。Java.util包当中,提供了这种方法,在notifyObservers的方法调用的时候,Observable调用Observers的update方法不传输任何数据信息,而是将自己的整个对象的引用传过去,而后Observers根据对象引用调取getter方法以获取想要的数据。
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData) {
WeatherData weatherData = (WeatherData) o;
this.temp = weatherData.getTemp();
this.humidity = weatherData.getHumidity();
this.display();
}
}
上面代码中,我们就可以看到,Observers并没有通过arg来获取数据,而是调用对象o中的getter方法来获取到了指定的数据。
/*
Java自带的Observer接口,同样定义了update方法,只不过传入的参数变了,
update(Observable o, Object arg)
数据以Object的形式传入,
Observable则用于Observer来识别接收到信息来自于哪一个subject。
*/
package WeatherApplicationWithJava;
import java.util.Observable;
import java.util.Observer;
public class CurrentConditionDisplay implements Observer {
private float temp;
private float humidity;
private Observable subject;
public CurrentConditionDisplay(Observable subject) {
this.subject = subject;
this.subject.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData) {
WeatherData weatherData = (WeatherData) o;
this.temp = weatherData.getTemp();
this.humidity = weatherData.getHumidity();
this.display();
}
}
public void display() {
System.out.println("Current Conditions: " + this.temp + " " + this.humidity);
}
}