设计模式(二)之观察者模式
场景描述
今天,我们接到一个需求。我们气象局需要给某个app提供一个接口。我们气象局通过各种感应装置,能够查到温度、湿度和气压等数据并且更新到三个不同的布告板上面。
分析
三个布告板需要三项数据,那么这个类weatherData类需要实现三个get,分别是getTEmperature、getHumidity和getPressure获取三项数据。然后通知给三个布告板,我们气象局数据已经更新了,需要创建一个方法measurementsChanged。获取三项数据十分简单,但是我们应该如何去实现这个通知方法呢?
设计
我们已经的到的方法
1.get方法
2.更新的时候触发measurementsChanged方法
然后,我们需要实现布告板以显示更新后的数据display。我们的布告板是需要可扩展的,因为在未来可能不止三个布告板,我们应该是可以新增加的。
错误实现
我们很容易想到是不是我们可以在measurementsChanged方法里面,手动获取到三项数据然后手动更新到布告板的类上面?

改变的地方,需要封装气啦
如果像上面一样的话,代码就变死了,因为它不具备可扩展性。我们可以看到三个布告板调用的方法貌似有点相似,甚至可以说一模一样。其实我们就可以抽象为接口,这样的话我们就可以面向接口编程,利用多态的特性,来遍历即可。
观察者模式
描述
在这里我们的weatherData就是下图的主题,各种对象就是我们的观察者。当数据改变的时候,主题就会通知观察者。
过程
1.主题对象需要管理这些观察者对象。(可以联想到你去报刊社订阅报刊,报刊社就会有记录,里面有很多订阅者的信息)
2.当主题对象改变的时候,主题就会把信息推送给观察者(报刊社送新一期的杂志给你)
3.没有注册的对象就不是观察者,那么在推送的时候,自然也不会推送给这些对象。

具体定义
定义了对象之间的一对多依赖,这样一来,当对象改变状态的时候,它的所有依赖者都会收到通知并更新。

实现类图


代码实现
主题
package com.yyh.observer;
/**
* 主题
*/
public interface Subject {
/**
* 注册观察者
* @param observer
*/
public void registerObserver(Observer observer);
/**
* 移除观察者
* @param observer
*/
public void removeObserver(Observer observer);
/**
* 通知观察者
*/
public void notifyObserver();
}
观察者
package com.yyh.observer;
/**
* 观察者
*/
public interface Observer {
/**
* 更新观察者
* @param temperature
* @param humidity
* @param pressure
*/
public void update(float temperature,float humidity,float pressure);
}
显示接口
方便以后,统一遍历。当然在这个例子里面可有可无
package com.yyh.observer;
/**
* 观察者
*/
public interface Observer {
/**
* 更新观察者
* @param temperature
* @param humidity
* @param pressure
*/
public void update(float temperature,float humidity,float pressure);
}
主题实现---weatherData
package com.yyh.observer;
import java.util.ArrayList;
import java.util.List;
public class WeatherData implements Subject {
//存储所有的观察者,注意类型是observer,里面有方法update
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
/**
* 初始化list,方便存储
*/
public WeatherData() {
this.observers = new ArrayList<>();
}
/**
* 注册观察者
* @param observer
*/
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
/**
* 删除观察者
* @param observer
*/
@Override
public void removeObserver(Observer observer) {
int i = observers.indexOf(observer);
if (i>0){
observers.remove(observer);
}
}
/**
* 通知观察者
*/
@Override
public void notifyObserver() {
for (int i = 0; i < observers.size(); i++) {
Observer temp = observers.get(i);
temp.update(temperature,humidity,pressure);
}
}
/**
* 通知观察者
*/
public void measurementsChanged(){
notifyObserver();
}
/**
* 测试所用手动设置数据,并调用方法
* @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();
}
}
观察者实现---布告栏
package com.yyh.observer;
public class CurrentConditionDisplay implements Observer,DisplayElement {
private float temperature;
private float humidity;
private float pressure;
//方便观察者注册和删除
private Subject subject;
public CurrentConditionDisplay(Subject subject) {
this.subject = subject;
//注册当前布告板
subject.registerObserver(this);
}
@Override
public String toString() {
return "CurrentConditionDisplay{" +
"temperature=" + temperature +
", humidity=" + humidity +
", pressure=" + pressure +
'}';
}
@Override
public void display() {
System.out.println(toString());
}
@Override
public void update(float temperature, float humidity, float pressure) {
//设置参数
this.humidity = humidity;
this.pressure = pressure;
this.temperature = temperature;
//显示数据
display();
}
}
测试类
package com.yyh.observer;
public class TestMain {
public static void main(String[] args) {
//创建WeatherData类,主题
WeatherData weatherData = new WeatherData();
//创建布告栏
CurrentConditionDisplay currentConditionDisplay = new CurrentConditionDisplay(weatherData);
//修改数据
weatherData.setMeasurements(80,10,80);
//修改数据
weatherData.setMeasurements(70,10,80);
//修改数据
weatherData.setMeasurements(70,10,80);
}
}
测试结果

从main里面看十分神奇~~~
扩展
在Java里面很多地方会用到观察者模式,我们最熟悉不过的swing里面就有。当我们监听按钮的时候,jbutton对象有一个addActionListener方法。这里就运用到了观察者模式,我们操作的是按钮这个对象,按钮这个对象会通知你创建的这个匿名类进行操作!
并且java里面有两种帮你实现观察者模式的类,一个是java.util.Observable类,另一个是java.util.Observer接口。但是由于篇幅原因这里就不在赘述了。需要注意的是,观察者模式分为推和拉。我们上面实现的就是推。主题主动推送通知,接受者被动接受。另一种是接受者主动获取数据,二者有所不同。
如果你喜欢,那么点个喜欢,谢谢!喜欢是我更新的最大动力!