Java设计模式之观察者模式
代码应该如同晚霞中的莲花一样地关闭(免于改变),如同晨曦中的莲花一
样地开放(能够扩展)
定义
观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新,大致构成如下
总结:
应用场景
现在又如下需求:
天气气象局可以测量天气的温度、湿度和气压,当需要测量新的天气测量数据measurementsChanged() 方法就会被调用,现在有2个需要使用天气数据的通知板,如果天气数据有更新,需要马上通知这两个通知板,并且未来可能会有其他通知板加入进来
这时候你需要如何设计? 这就是典型的发布订阅场景,只需要通知板订阅天气气象局即可,如果某个通知板不想受到气象通知了,取消订阅即可
代码实现
首先定义个 Subject 接口,接口中有一些对观察者的一些操作具体看代码注释
Subject
/** * @author WH * @version 1.0 * @date 2020/3/23 22:12 * @Description 主题接口 */
public interface Subject {
/** * 注册观察者 * @param o */
void registerObserver(Observer o);
/** * 删除观察者 * @param o */
void removeObserver(Observer o);
/** * 通知观察者 */
void notifyObservers();
}
WeatherData类,Subject 的具体实现
WeatherData
/** * @author WH * @version 1.0 * @date 2020/3/23 22:23 * @Description TODO */
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList();
}
@Override
public void registerObserver(Observer o) {
System.out.println("添加观察者");
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(o);
}
}
@Override
public void notifyObservers() {
observers.forEach(s -> s.update(temperature, humidity, pressure));
}
/** * 通知给观察者 */
public void measurementsChanged() {
notifyObservers();
}
/** * 更新天气数据测试方法 * @param temperature 温度 * @param humidity 湿度 * @param pressure 气压 */
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
这里再定义一个观察者接口Observer,用于观察者订阅消息的实现,即消息有update需要调用的方法
Observer
/** * @author WH * @version 1.0 * @date 2020/3/23 22:14 * @Description 观察者接口 */
public interface Observer {
/** * 抽象观察者具体实现 * @param temp * @param humidity * @param pressure */
void update(float temperature, float humidity, float pressure);
}
为了统一控制通知板的通知消息,我们抽象出一个DisplayElement接口出来控制订阅办的打印消息
DisplayElement
/** * @author WH * @version 1.0 * @date 2020/3/23 22:21 * @Description TODO */
public interface DisplayElement {
/** * 布告板需要显示调用次方法 */
void display();
}
ForecastDisplay
现在是创建具体的观察者,观察者都应该实现Observer和DisplayElement接口
/** * @author WH * @version 1.0 * @date 2020/3/23 23:07 * @Description TODO */
public class ForecastDisplay implements Observer, DisplayElement {
private float temperature;
private float pressure;
private Subject weatherData;
public ForecastDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("Current conditions: " + temperature
+ "F degrees and " + pressure + "% pressure");
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.pressure = pressure;
display();
}
}
/** * @author WH * @version 1.0 * @date 2020/3/23 22:49 * @Description 目前天气布告板 */
public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
/** * 注册为观察者 * @param weatherData */
public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
/** * 显示最近温度湿度 */
@Override
public void display() {
System.out.println("Current conditions: " + temperature
+ "F degrees and " + humidity + "% humidity");
}
/** * 当调用update,将温度和湿度保存下来 * @param temperature * @param humidity * @param pressure */
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
}
测试
/** * @author WH * @version 1.0 * @date 2020/3/23 22:54 * @Description TODO */
public class Test {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay current = new CurrentConditionsDisplay(weatherData);
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}
如果我们需要添加新的通告版,只需要实现这两个接口,就可以自动通知了,如果我们其中有通告版不想受到消息了,移除自己即可,这就是观察者模式
Java内置的观察者模式
Java内置API支持实现观察者模式,其中观察者只需要实现内置的类Observer,之前的WeatherData也主需要继承内置Observable,用到了继承就知道以后的扩展肯定不会太好
大致改动如下
WeatherData
public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure;
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = 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();
}
}
CurrentConditionsDisplay
public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Observable observable;
/** * 登记为观察者 * @param observable */
public CurrentConditionsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void display() {
System.out.println("Current conditions: " + temperature
+ "F degrees and " + 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();
}
}
}
还没有评论,来说两句吧...