出版者+订阅者=观察者模式
出版者:主题 Subject
订阅者:观察者 Observer
观察者模式
在对象之间定义一对多的依赖关系,当一个对象(主题)改变状态时,它的所有依赖者(观察者)都将收到通知,并自动更新。
主题与观察者之间通过组合关系完成相互的调用:
所有观察者Observer持有相同的一个主题Subject,观察者通过该接口向主题进行注册;
主题Subject持有观察者的一个集合List<Observer>,主题遍历集合向每个观察者发送通知
设计原则
为了交互对象之间的松耦合设计而努力;
找出程序中会变化的方面,然后将其和固定不变的方面相分离。(观察者的类型和个数是变化的);
针对接口编程,不针对实现编程。(主题与观察者都是接口,主题也可以有多个实现);
多用组合,少用继承。(通过依赖完成关系的建立);
================================================================================
实例
主题接口
package Subject;
import Observer.Observer;
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
具体的主题
package Subject;
import java.util.ArrayList;
import java.util.List;
import Observer.Observer;
public class WeatherData implements Subject {
private float temperature;
private float humidity;
private float pressure;
//一对多关系
//观察者的集合
private List<Observer> observers;
public WeatherData() {
this.observers = new ArrayList<Observer>();
}
/**
* 注册到主题中
*/
@Override
public void registerObserver(Observer o) {
this.observers.add(o);
}
/**
* 取消订阅
*/
@Override
public void removeObserver(Observer o) {
int index = this.observers.indexOf(o);
if(index!=-1)
this.observers.remove(o);
}
/**
* 一旦状态发生变化,通知所有的观察者
*/
@Override
public void notifyObservers() {
for(Observer o : observers)
o.update(temperature, humidity, pressure);
}
public void measurementChanged() {
notifyObservers();
}
public void setMessurements(float temprature, float humidity, float pressure) {
this.temperature = temprature;
this.humidity = humidity;
this.pressure = pressure;
measurementChanged();
}
}
观察者的接口
package Observer;
public interface Observer {
/**
* 观察者提供给主题使用的接口
*/
public void update(float temp, float humidity, float pressure);
}
观察者需要实现的另一个接口-布告栏接口
package display;
public interface DisplayElement {
public void display();
}
具体的观察者
package Observer;
import Subject.Subject;
import display.DisplayElement;
public class CurrentConditionDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
//持有主题的引用,完成对主题的注册与取消订阅
private Subject weatherData;
public CurrentConditionDisplay(Subject weatherData) {
this.weatherData = weatherData;
//注册
this.weatherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
@Override
public void display() {
System.out.println("Current Condition: tempratute="+temperature+", humidity="+humidity);
}
}
package Observer;
import Subject.Subject;
import display.DisplayElement;
public class StatisticsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
//持有主题的引用,完成对主题的注册与取消订阅
private Subject weatherData;
public StatisticsDisplay(Subject weatherData) {
this.weatherData = weatherData;
//注册
this.weatherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
@Override
public void display() {
System.out.println("StatisticsDisplay: tempratute="+temperature+", humidity="+humidity);
}
}
package Observer;
import Subject.Subject;
import display.DisplayElement;
public class ForeastDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
//持有主题的引用,完成对主题的注册与取消订阅
private Subject weatherData;
public ForeastDisplay(Subject weatherData) {
this.weatherData = weatherData;
//注册
this.weatherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
@Override
public void display() {
System.out.println("ForeastDisplay: tempratute="+temperature+", humidity="+humidity);
}
}
测试
package test;
import Observer.CurrentConditionDisplay;
import Observer.ForeastDisplay;
import Observer.StatisticsDisplay;
import Subject.WeatherData;
public class WeatherDataTest {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
ForeastDisplay foreastDisplay = new ForeastDisplay(weatherData);
weatherData.setMessurements(31.03f, 29.33f, 100.21f);
//取消订阅的职责是否应该放到观察者中进行更合适?
weatherData.removeObserver(foreastDisplay);
System.out.println("remove foreastDisplay...");
weatherData.setMessurements(22.03f, 33.33f, 44.21f);
}
}
================================================================================
使用JAVA内置的观察者模式
Observable的黑暗面---Observable是一个类而不是一个接口,它甚至没有实现一个接口。
如果要使用JAVA提供的这个Observable的行为,只能通过继承才能得到,这样便让子类非常的为难,最终限制了Observable的复用潜力。
setChanged()在Observable类中被保护起来了,除非继承Observable,否则无法创建Observable实例并组合到自己的对象中。
在子类中,通过Observable类中protected权限的setChanged()方法,去改变Observable类中的私有属性boolean changed = true,完成状态的切换!!!
主题
package Subject;
import java.util.Observable;
/**
* 主题
* 主题继承JAVA提供的内置主题
* 由于JAVA提供的Observable是一个类,要想使用JAVA内置的这个功能,只能采用继承
*
*/
public class WeatherData2 extends Observable {
private float temperature;
private float humidity;
private float pressure;
public void measurementChanged() {
//指示状态已发生变化,可以通知观察者了。扩展点:这里可以设置条件,当条件满足时才发出通知,而不是每次变化都通知!
setChanged();
//通知所有的观察者
notifyObservers();
}
public void setMessurements(float temprature, float humidity, float pressure) {
this.temperature = temprature;
this.humidity = humidity;
this.pressure = pressure;
measurementChanged();
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
观察者需要实现的布告栏接口
package display;
public interface DisplayElement {
public void display();
}
观察者
package Observer;
import java.util.Observable;
import java.util.Observer;
import Subject.WeatherData2;
import display.DisplayElement;
/**
* 观察者
* 使用JAVA提供的内置观察者接口
*
*/
public class CurrentConditionDisplay2 implements Observer, DisplayElement {
//主题接口
Observable observable;
private float temperature;
private float humidity;
public CurrentConditionDisplay2(Observable observable){
//为主题接口指定具体实现类
this.observable = observable;
//将观察者注册到主题中
this.observable.addObserver(this);
}
/**
* 采用拉的方式,由观察者自己到主题中获取需要的数据
* @param o 主题
* @param arg 主题“推送”的数据,如果不为空,则表示由主题负责“推送”数据
*/
@Override
public void update(Observable o, Object arg) {
if(o instanceof WeatherData2) {
WeatherData2 wd = (WeatherData2)o;
this.temperature = wd.getTemperature();
this.humidity = wd.getHumidity();
display();
}
}
@Override
public void display() {
System.out.println("Current Condition: tempratute="+temperature+", humidity="+humidity);
}
}
测试
package test;
import Observer.CurrentConditionDisplay2;
import Subject.WeatherData2;
public class WeatherData2Test {
public static void main(String[] args) {
WeatherData2 weatherData = new WeatherData2();
CurrentConditionDisplay2 currentDisplay = new CurrentConditionDisplay2(weatherData);
weatherData.setMessurements(31.03f, 29.33f, 100.21f);
//取消订阅的职责是否应该放到观察者中进行更合适?
weatherData.deleteObserver(currentDisplay);
System.out.println("remove foreastDisplay...");
weatherData.setMessurements(22.03f, 33.33f, 44.21f);
}
}