一、认识观察者模式
主题+订阅者=观察者模式
观察者模式中,一个被观察者管理所有相依与它的观察者物件,并且在本身的状态改变时主动发出通知。这通常通过呼叫各个观察者所提供的的方法来实现。通常用于实现事件处理系统。
观察者模式定义了对象之间的一对多依赖关系,这样依赖当一个对象的状态发生改变时它的依赖者都会收到通知并更新。
二、观察者模式的基本结构
观察者模式UML图
Subject:主题。他把所有对观察者对象的引用保存在一个集合里,每一个主题都可以有多个观察者。
Observer:观察者。为所有的具体观察者定义一个接口,在得到主题的通知时能够及时的更新自己。
ConcreteSubject:具体主题。将有关状态存入具体观察者对象。在具体主题发生改变时,给所有订阅改主题的观察者发出通知。
ConcreteObserver:具体观察者。实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态相协调。
三、观察者模式的具体实现
代码实现
Subject 主题接口
package com.devil.designpattern.observerpattern.inter;
/**
* 主题
* Created by wangdi on 2016/11/30.
*/
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObserver();
}
Observer 观察者接口
package com.devil.designpattern.observerpattern.inter;
/**
* 观察者
* Created by wangdi on 2016/11/30.
*/
public interface Observer {
void update(float temp, float humidity, float pressure);
}
DisplayElement 公告板显示接口
package com.devil.designpattern.observerpattern.inter;
/**
* Created by wangdi on 2016/11/30.
*/
public interface DisplayElement {
void display();
}
WeatherData 天气数据类
package com.devil.designpattern.observerpattern.domain;
import com.devil.designpattern.observerpattern.inter.Observer;
import com.devil.designpattern.observerpattern.inter.Subject;
import java.util.ArrayList;
import java.util.List;
/**
* Created by wangdi on 2016/11/30.
*/
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
this.observers = new ArrayList<Observer>();
}
public void registerObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
if (observers.contains(observer)) {
observers.remove(observer);
}
}
public void notifyObserver() {
observers.forEach(observer -> observer.update(temperature, humidity, pressure));
}
public void measurementsChanged() {
notifyObserver();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
CurrentConditionsDisplay 当前观测值公告板类
package com.devil.designpattern.observerpattern.domain;
import com.devil.designpattern.observerpattern.inter.DisplayElement;
import com.devil.designpattern.observerpattern.inter.Observer;
import com.devil.designpattern.observerpattern.inter.Subject;
/**
* 当前观测值公告板类
* Created by wangdi on 2016/11/30.
*/
public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("Current conditions:");
System.out.println("temperature:" + temperature);
System.out.println("humidity:" + humidity);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
}
ForecastDisplay 气压值公告板类
package com.devil.designpattern.observerpattern.domain;
import com.devil.designpattern.observerpattern.inter.DisplayElement;
import com.devil.designpattern.observerpattern.inter.Observer;
import com.devil.designpattern.observerpattern.inter.Subject;
/**
* 气压值公告板类
* Created by wangdi on 2016/11/30.
*/
public class ForecastDisplay implements Observer, DisplayElement {
private float pressure;
private Subject weatherData;
public ForecastDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("Forecast conditions:");
System.out.println("pressure:" +pressure);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.pressure = pressure;
display();
}
}
StatisticsDisplay 显示观测值的最大值、最小值、平均值类
package com.devil.designpattern.observerpattern.domain;
import com.devil.designpattern.observerpattern.inter.DisplayElement;
import com.devil.designpattern.observerpattern.inter.Observer;
import com.devil.designpattern.observerpattern.inter.Subject;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* 显示观测值的最大值、最小值、平均值类
* Created by wangdi on 2016/11/30.
*/
public class StatisticsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public StatisticsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("Statistics condition");
getMax(temperature, humidity, pressure);
getMin(temperature, humidity, pressure);
getAverage(temperature, humidity, pressure);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
this.pressure = pressure;
display();
}
private void getMax(float temp, float humidity, float pressure) {
List<Float> list = Arrays.asList(temp, humidity, pressure);
System.out.println("Max:" + Collections.max(list));
}
private void getMin(float temp, float humidity, float pressure) {
List<Float> list = Arrays.asList(temp, humidity, pressure);
System.out.println("Min:" + Collections.min(list));
}
private void getAverage(float temp, float humidity, float pressure) {
List<Float> list = Arrays.asList(temp, humidity, pressure);
System.out.println("Average:" + (temp + humidity + pressure) / 3);
}
}
WeatherStation 气象站类
package com.devil.designpattern.observerpattern.domain;
/**
* 气象站类
* Created by wangdi on 2016/11/30.
*/
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
weatherData.setMeasurements(88,55,66);
}
}
输出结果
四、使用Java内置的观察者模式
java.util包内包含最基本的Observer接口与Observable类,和自己实现的Subject接口和Observer接口相似。Observer接口与Observable类使用起来更方便因为许多功能都已经实现准备好了。当状态更新时,甚至可以使用推(push)或拉(pull)的方式传送数据。
使用Java内置的观察者模式修改以后的类图
代码实现
CurrentConditionsDisplay 当前观测值公告板类
package com.devil.designpattern.jdkobserverpattern;
import com.devil.designpattern.observerpattern.inter.DisplayElement;
import java.util.Observable;
import java.util.Observer;
/**
* 当前观测值公告板类
* Created by wangdi on 2016/11/30.
*/
public class CurrentConditionsDisplay implements Observer, DisplayElement {
private Observable observable;
private float temperature;
private float humidity;
public CurrentConditionsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void display() {
System.out.println("Current conditions:");
System.out.println("temperature:" + temperature);
System.out.println("humidity:" + humidity);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData) {
WeatherData weatherData = (WeatherData) o;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
}
ForecastConditionsDisplay 气压值公告板类
package com.devil.designpattern.jdkobserverpattern;
import com.devil.designpattern.observerpattern.inter.DisplayElement;
import java.util.Observable;
import java.util.Observer;
/**
* 气压值公告板类
* Created by wangdi on 2016/11/30.
*/
public class ForecastConditionsDisplay implements Observer, DisplayElement {
private float pressure;
private Observable observable;
public ForecastConditionsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void display() {
System.out.println("Forecast conditions:");
System.out.println("pressure:" +pressure);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData) {
WeatherData weatherData = (WeatherData) o;
this.pressure = weatherData.getPressure();
display();
}
}
}
StatisticsConditionsDisplay 显示观测值的最大值、最小值、平均值类
package com.devil.designpattern.jdkobserverpattern;
import com.devil.designpattern.observerpattern.inter.DisplayElement;
import java.util.*;
/**
* 显示观测值的最大值、最小值、平均值类
* Created by wangdi on 2016/11/30.
*/
public class StatisticsConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private float pressure;
private Observable observable;
public StatisticsConditionsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void display() {
System.out.println("Statistics condition");
getMax(temperature, humidity, pressure);
getMin(temperature, humidity, pressure);
getAverage(temperature, humidity, pressure);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData) {
WeatherData weatherData = (WeatherData) o;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
this.pressure = weatherData.getPressure();
display();
}
}
private void getMax(float temp, float humidity, float pressure) {
List<Float> list = Arrays.asList(temp, humidity, pressure);
System.out.println("Max:" + Collections.max(list));
}
private void getMin(float temp, float humidity, float pressure) {
List<Float> list = Arrays.asList(temp, humidity, pressure);
System.out.println("Min:" + Collections.min(list));
}
private void getAverage(float temp, float humidity, float pressure) {
List<Float> list = Arrays.asList(temp, humidity, pressure);
System.out.println("Average:" + (temp + humidity + pressure) / 3);
}
}
WeatherData 天气数据类
package com.devil.designpattern.jdkobserverpattern;
import java.util.Observable;
/**
* 天气数据类
* Created by wangdi on 2016/11/30.
*/
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;
}
}
WeatherStation 气象站类
package com.devil.designpattern.jdkobserverpattern;
/**
* 气象站类
* Created by wangdi on 2016/11/30.
*/
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
ForecastConditionsDisplay forecastConditionsDisplay = new ForecastConditionsDisplay(weatherData);
StatisticsConditionsDisplay statisticsConditionsDisplay = new StatisticsConditionsDisplay(weatherData);
weatherData.setMeasurements(88, 55, 66);
}
}
输出结果
两次结构的文字输出顺序是不一样的,所以不能依赖于观察者被通知的次序。如果我们的代码依赖于这样的次序,这个错的。因为一旦观察者/被观察者的实现有所改变,通知次序就会改变,很可能就会产生错误的结果。这并不是我们所认为的送耦合。
Observable是一个类
但是,java.util.Observable是一个类而不是接口,限制了它的使用和复用。当一个类想同时具有Observable类和另一个类的超类的行为,这是不允许的,Java不支持多重继承。这限制了Observable的复用。
Observable讲关键的方法保护起来
当看Observable API,会发现setChanged()方法被保护起来了(被定义成protected)这就以为着除非继承Observable,否则无法创建Observable实例组合到自己的对象中来。这个设计违反了第二个设计原则:“多用组合,少用继承”。
项目代码示例
https://github.com/code-wangdi/Design-Patterns/tree/master/Observer-Pattern