问题引出
气象站需要实时播报天气变化情况,需要我们帮助他设计一个方案解决这个问题
普通方法
设计一个WeatherData类,里面包含天气的各种属性,同时气象站实时监控天气,通过定时器每隔一段时间检测一次天气是否变化
问题:这样设计的话,需要单独开启一个线程,不停地监听对象的状态,如果在一个负载的系统中,可能会因此开启很多线程来实现这一功能,这将使系统的性能产生额外的负担
解决方案:通过天气类想气象局推送信息
每当天气发生变化时,WeatherData像气象局推送天气相关信息,气象局更新气象信息
问题升级
有很多网站需要获取气象信息实时显示,应该怎么办?
这时可以使用推送,但当网站增加时,无法动态加入该网站,违反了开闭原则
解决办法:观察者模式
观察者模式好处:可以在单线程中使某一对象及时得知自身所依赖的状态的变化
此时各网站就是观察者,天气就是被观察者
// 被观察者接口,让WeatherData(被观察者)来实现
public interface Subject {
public void addObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}
// 具体被观察者(维护与观察者之前的关系)
// 观察者模式是一种一对多的模式,一方来维护关系
public class WeatherData implements Subject {
private float temperature;
private float pressure;
private float humidity;
// 观察者集合
private ArrayList<Observer> observers;
public WeatherData() {
observers = new ArrayList<Observer>();
}
public void dataChange() {
notifyObserver();
}
public void setData(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
dataChange();
}
// 注册一个观察者
@Override
public void addObserver(Observer o) {
observers.add(o);
}
// 移除一个观察者
@Override
public void removeObserver(Observer o) {
if (observers.contains(o)) {
observers.remove(o);
}
}
@Override
public void notifyObserver() {
for(Observer observer : observers) {
observer.update(this.temperature, this.pressure, this.humidity);
}
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
public ArrayList<Observer> getObservers() {
return observers;
}
public void setObservers(ArrayList<Observer> observers) {
this.observers = observers;
}
}
// 观察者接口,由观察者来实现
public interface Observer {
public void update(float temperature, float pressure, float humidity);
}
// 具体观察者:某企鹅
public class TX implements Observer {
// 温度、气压、湿度
private float temperature;
private float pressure;
private float humidity;
@Override
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
public void display() {
System.out.println("腾讯提醒:当前温度 " + temperature);
System.out.println("腾讯提醒:当前压力 " + pressure);
System.out.println("腾讯提醒:当前湿度 " + humidity);
}
}
// 具体观察者:某度
public class Baidu implements Observer {
private float temperature;
private float pressure;
private float humidity;
@Override
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
public void display() {
System.out.println("BaiduSite提示:当前温度 " + temperature);
System.out.println("BaiduSite提示:当前压力 " + pressure);
System.out.println("BaiduSite提示:当前湿度 " + humidity);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
// 创建一个WeatherData
WeatherData weatherData = new WeatherData();
// 创建观察者
TX tx = new TX();
Baidu baidu = new Baidu();
// 注册到WeatherData
weatherData.addObserver(tx);
weatherData.addObserver(baidu);
// 测试
System.out.println("通知观察者");
weatherData.setData(4.3f, 98f, 30.4f);
// 移除观察者
weatherData.removeObserver(baidu);
System.out.println("");
// 第二次测试
System.out.println("再次通知观察者");
weatherData.setData(4.3f, 98f, 30.4f);
}
}
// 运行结果
通知观察者
腾讯提醒:当前温度 4.3
腾讯提醒:当前压力 98.0
腾讯提醒:当前湿度 30.4
BaiduSite提示:当前温度 4.3
BaiduSite提示:当前压力 98.0
BaiduSite提示:当前湿度 30.4
再次通知观察者
腾讯提醒:当前温度 4.3
腾讯提醒:当前压力 98.0
腾讯提醒:当前湿度 30.4
观察者模式好处:
- 使用观察者模式之后,被观察者会以集合的方式来管理观察者(Observer),包括注册、移除和通知
- 增加观察者时,就不需要修改被观察者(WeatherData)中的代码,遵守了开闭原则(ocp原则)
扩展
在java.util.Observable类中,已经实现了主要的功能,如增加观察者、删除观察者和通知观察者,我们可以通过继承Observable来使用这些功能
在JDK中,观察者模式也得到了普遍的应用,最典型的应用是Swing框架的JButton实现(监听器)