设计原则:实现对象之间的松耦合,交互对象彼此无需知道彼此实现细节。能应对变化,因为彼此的依赖降低到了最低。
另一个例子:订报。有两个角色,报社和订户。报社为主题对象,而订户为观察者。当报社有新的报纸产出(数据变化),会发送消息给相应的订户。如果订户接收,则接收报社的投递。如果订户不接受,则不接受报社的投递。
松耦合:两个对象可以相互交互,但是不清楚彼此的实现细节。
观察者模式:主体只知道观察者实现了一个接口observer。不知道观察者是谁,内部细节。?主体唯一依赖的东西是实现接口的对象列表?。所以可以在任意时候取代,删除,创建观察者。新的观察者的出现无需通知主体。主体不在乎别的,会给所有实现了该接口的观察者发送消息。我们可以独立的复用观察者和主体,因为它们是耦合的。
问题引出:有个气象站,其硬件设备可以测量压强,气温,湿度。这些数据可计算得到目前天气,预测天气,统计天气三个产出。当压强气温湿度数据发生变化后,产出数据会立即变化。产出可以得到拓展,即存在更多形式的产出类型。
原始解决方案:
先定义天气信息类,注意引入了一个产出牌的类:
public class WeatherData {
private float data1;
private float data2;
private float data3;
private CurrentWeatherCondition c;
public WeatherData(CurrentWeatherCondition c){
this.c=c;
}
public void dataUpdate(float data1,float data2,float data3){
this.data1=data1;this.data2=data2;this.data3=data3;
c.update(data1,data2,data3);
}
//省略了setter/getter方法
}
产出类:
public class CurrentWeatherCondition {
public void update(float data1,float data2,float data3){
display();
System.out.print(123);
}
public void display(){
}
}
测试:
public class UserInterface {
public static void main(String args[]) {
CurrentWeatherCondition c = new CurrentWeatherCondition();
WeatherData weatherData = new WeatherData(c);
weatherData.dataUpdate(1,2,3);
}
}
显然是可以通过测试的。然而这种方式类的耦合程度太高。在运用中,当需要使用到新的产出类型的时候相当麻烦的大懂干戈。可扩展性。
使用观察者模式来解决这个问题:
观察者模式:定义了对象间的一对多关系。当一个对象的状态发生改变的时候,所有的依赖者收到通知并自动更新。
WeatherData为主体,观察者为布告。布告必须注册,主体获知布告的存在后,调用布告的方法update()告知数据变化。布告板有差异,共同去实现一个接口好让主体感知。
有一个重要的事情要额外注意的是,我们无法获知布告板的数目。
首先定义三个接口:分别为主体,观察者,最后为观察者需要处理的一个display(不作为观察者模式的重点)
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObservers();
}
public interface Observer {
public void update(float data1,float data2,float data3);
}
public interface DisplayElement {
public void display();
}
主体类:
public class WeatherData implements Subject {
//情理之中又意料之外的使用了list
private ArrayList observers;
private float data1;
private float data2;
private float data3;
public WeatherData(){
observers=new ArrayList();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
//注意这种写法
int i=observers.indexOf(observer);
if (i>=0){
observers.remove(i);
}
}
@Override
public void notifyObservers() {
for (int i=0;i<observers.size();i++){
//注意这种写法
Observer o=(Observer) observers.get(i);
//此处是重点,多态
o.update(data1,data2,data3);
}
}
public void dataChanged(){
notifyObservers();
}
public void setData(float data1,float data2,float data3){
this.data1=data1;this.data2=data2;this.data3=data3;
dataChanged();
}
}
对于观察者:
public class CurrentConditionDisplay implements Observer,DisplayElement {
private float data1;
private float data2;
//之所以保留subject的引用是为了当出现需要取消注销的时候方便
private Subject weatherData;
//注意此处的逻辑,我们是后期开发多的一端
public CurrentConditionDisplay(Subject weatherData){
this.weatherData=weatherData;
//重点
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("helloworld!");
}
@Override
public void update(float data1, float data2, float data3) {
this.data1=data1;
this.data2=data2;
display();
}
}
测试:
public class UserInterface {
public static void main(String args[]){
WeatherData weatherData=new WeatherData();
CurrentConditionDisplay currentConditionDisplay=new CurrentConditionDisplay(weatherData);
weatherData.setData(1,1,1);
}
}
实际上Java内置观察者模式。java.util包中包含最基本的observer和observable(可观察者)类。我们甚至可以只用拉,推的方式获取数据。有了内置的帮助,我们实现了observable后,告诉它如何通知观察者即可。
Java内置的观察者模式:observable是类,不是接口。
如何把对象变成可观察者:实现观察者接口,调用注册方法成为观察者,也可remove。
可观察者如何送出通知:继承observable类产生可观察类,调用setChanged()方法声明状态改变,调用notifyObservers()方法或notifyObservers(Object arg)方法。
观察者如何收到消息:观察者实现了更新方法update(Observable o,Object arg)
推、拉两种方式实现信息交互:
若想推push数据给观察者,则使用notifyObservers(Object arg),把数据传输。
若观察者想拉pull数据:
可观察者类:注意继承类,我们无需注册和删除观察者部分,直接被继承。可观察者无需建立数据结构存储观察者。
import java.util.Observable;
public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure;
public WeatherData() { }
public void measurementsChanged() {
setChanged();//注意没有提供数据进来,所以是拉的方式而不是推。
notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
观察者:注意update方法。
public class CurrentConditionsDisplay implements Observer, DisplayElement {
Observable observable;
private float temperature;
private float humidity;
public CurrentConditionsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
public void update(Observable obs, Object arg) {
if (obs instanceof WeatherStationObservable.WeatherData) {
WeatherData weatherData = (WeatherData)obs;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
public void display() {
System.out.println("Current conditions: " + temperature
+ "F degrees and " + humidity + "% humidity");
}
}
测试:
public class UserInterface {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentConditions = new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}
观察者模式定义了对象的一对多的关系。可观察者用一个共同的接口更新观察者。观察者和可观察者松耦合的关系,可观察者不知道观察者的细节,只知道实现了某接口。
可以使用推拉两种方式实现数据交互,推的方式更适合。有多个观察者的时候,若使用了Java内置方法,无法保证顺序。
注意:Java内置的观察者相关接口和类问题:可观察者需要继承一个类,架构不太合适(少继承多组合,Java仅支持单继承),可观察者维护的观察者列表在notifyObservers处顺序会出现乱。实现自身的观察者模式效果更好。