观察者模式(Observer Pattern) – 设计模式之行为模式:
目录
观察者模式(Observer Pattern)
定义:Define a one-to-many dependency between object changes state, all its dependents are notified and updated automatically.
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有的观察者对象,使它们自动更新自己。
类图
在中介中模式中,用到的是消息通信的例子,同样有注册,发布消息和通知的内容。跟观察者模式有什么区别吗? 观察者是单向的,比如:新闻部发布了新闻,然后告知订阅的人。订阅的人跟发布者之间不存在其它关联,比如中介者的两者相互通信。
例子-新闻:
过程:
在海贼王中,摩根斯是世界经济新闻社的社长,很多海贼和海军都订阅了他的报纸。新闻社发布了新的新闻,订阅者都会收到通知。
类图:
代码:
被观察者 INewsSubject
定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。
public interface INewsSubject {
void registerObserver(INewsObserver observer);
void removeObserver(INewsObserver observer);
//通知
void notifyObserver();
}
被观察者的职责非常简单,就是定义谁能够观察,谁不能观察,程序中使用ArrayList和Vector没有太大的差别,ArrayList是线程异步,不安全;Vector是线程同步,安全——就这点区别。
具体被观察者 MogensiNewsSubject
定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。
/**
* 摩根斯新闻社
*/
public class MogensiNewsSubject implements INewsSubject {
// 观察者集合
private List<INewsObserver> observerList = new ArrayList<>();
// 新闻
private String newsMsg;
@Override
public void registerObserver(INewsObserver observer) {
observerList.add(observer);
}
@Override
public void removeObserver(INewsObserver observer) {
observerList.remove(observer);
}
@Override
public void notifyObserver() {
for (int i = 0; i < observerList.size(); i++) {
observerList.get(i).updateNews(newsMsg);//給每一个观察者刷新一下
}
}
public void setMessage(String newsMsg) {
this.newsMsg = newsMsg;
System.out.println("摩根斯新闻社发布了一条新闻:"+newsMsg);
}
}
观察者 INewsObserver
观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理
public interface INewsObserver {
void updateNews(String msg);
}
观察者一般是一个接口,每一个实现该接口的实现类都是具体观察者,
具体观察者 PirateObserver
每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑
public class PirateObserver implements INewsObserver {
private String name;
public PirateObserver(String name) {
this.name = name;
}
@Override
public void updateNews(String msg) {
System.out.println(name + " 收到一条新闻:" + msg);
}
}
测试:
public class ObserverTest {
public static void main(String[] arg) {
//创建摩根斯新闻社
MogensiNewsSubject newsSubject = new MogensiNewsSubject();
// 看新闻的
INewsObserver strawHat = new PirateObserver("路飞");
INewsObserver redHaired = new PirateObserver("红发");
INewsObserver dragon = new PirateObserver("龙");
// 注册看新闻的
newsSubject.registerObserver(strawHat);
newsSubject.registerObserver(redHaired);
newsSubject.registerObserver(dragon);
newsSubject. setMessage("大新闻,萨博被绿牛和藤虎联合啥了");
newsSubject.notifyObserver();
}
}
结果:
摩根斯新闻社发布了一条新闻:大新闻,萨博被绿牛和藤虎联合啥了
路飞 收到一条新闻:大新闻,萨博被绿牛和藤虎联合啥了
红发 收到一条新闻:大新闻,萨博被绿牛和藤虎联合啥了
龙 收到一条新闻:大新闻,萨博被绿牛和藤虎联合啥了
例子-天气:
过程:
在福建各地市关注天气的变化。
类图:
代码:
具体被观察者 WeatherSubject
public class WeatherSubject {
private WeatherTypeEnum currentWeather;
private final List<IWeatherObserver> observers;
private WeatherTypeEnum[]enumValues = WeatherTypeEnum.values();
public Weather() {
observers = new ArrayList<>();
currentWeather = WeatherTypeEnum.SUNNY;
}
public void addObserver(IWeatherObserver obs) {
observers.add(obs);
}
public void removeObserver(IWeatherObserver obs) {
observers.remove(obs);
}
/**
* ordinal按照它的规则给每个枚举变量按顺序赋值。
*/
public void weatherChange() {
System.out.println(currentWeather.ordinal());
currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length];
System.out.println("天气变化为: "+currentWeather);
notifyObservers();
}
private void notifyObservers() {
for (IWeatherObserver obs : observers) {
obs.update(currentWeather);
}
}
}
天气枚举类: WeatherTypeEnum
public enum WeatherTypeEnum {
SUNNY("晴天"),
RAINY("下雨"),
WINDY("大风"),
SNOWY("下雪"),
CLOUDY("阴天");
private final String description;
WeatherTypeEnum(String description) {
this.description = description;
}
public String getDescription() {
return this.description;
}
@Override
public String toString() {
return this.description;
}
}
观察者 IWeatherObserver
public interface IWeatherObserver {
void update(WeatherTypeEnum currentWeather);
}
具体观察者 FuzouCity
每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑
public class FuzouCity implements IWeatherObserver {
@Override
public void update(WeatherTypeEnum currentWeather) {
System.out.println("福州现在的天气: " + currentWeather.getDescription());
}
}
具体观察者 XiamenCity
public class XiamenCity implements IWeatherObserver {
@Override
public void update(WeatherTypeEnum currentWeather) {
System.out.println("厦门现在的天气: " + currentWeather.getDescription() );
}
}
具体观察者 QuanzhouCity
public class QuanzhouCity implements IWeatherObserver {
@Override
public void update(WeatherTypeEnum currentWeather) {
System.out.println("泉州现在的天气: " + currentWeather.getDescription() );
}
}
测试:
public class WeatherTest {
public static void main(String[] args) {
WeatherSubject weatherSubject = new WeatherSubject();
IWeatherObserver FUZHOU = new FuzouCity();
IWeatherObserver XIAMEN = new XiamenCity();
IWeatherObserver QUANZHOU = new QuanzhouCity();
weatherSubject.addObserver(FUZHOU);
weatherSubject.addObserver(XIAMEN);
weatherSubject.addObserver(QUANZHOU);
weatherSubject.weatherChange();
weatherSubject.weatherChange();
weatherSubject.weatherChange();
weatherSubject.removeObserver(QUANZHOU);
weatherSubject.weatherChange();
}
}
结果:
0
天气变化为: 下雨
福州现在的天气: 下雨
厦门现在的天气: 下雨
泉州现在的天气: 下雨
1
天气变化为: 大风
福州现在的天气: 大风
厦门现在的天气: 大风
泉州现在的天气: 大风
2
天气变化为: 下雪
福州现在的天气: 下雪
厦门现在的天气: 下雪
泉州现在的天气: 下雪
3
天气变化为: 阴天
福州现在的天气: 阴天
厦门现在的天气: 阴天
总结:
优点:
1、观察者和被观察者是抽象耦合的。
2、建立一套触发机制。
缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景
1 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。
2 事件多级触发场景。
3 跨系统的消息交换场景,如消息队列的处理机制。
D,注意事项
1 广播链的问题
在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次),这还是比较好控制的。
注意 它和责任链模式的最大区别就是观察者广播链在传播的过程中消息是随时更改的,它是由相邻的两个节点协商的消息结构;而责任链模式在消息传递过程中基本上保持消息不可变,如果要改变,也只是在原有的消息上进行修正。
2 异步处理问题
这个EJB是一个非常好的例子,被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的问题,这个大家有时间看看Message类 Queue类,就会有更深的了解。
最佳实践
观察者模式在实际项目和生活中非常常见,我们举几个经常发生的例子来说明。
1 文件系统
比如,在一个目录下新建立一个文件,这个动作会同时通知目录管理器增加该目录,并通知磁盘管理器减少1KB的空间,也就说“文件”是一个被观察者,“目录管理器”和“磁盘管理器”则是观察者。
2 猫鼠游戏
夜里猫叫一声,家里的老鼠撒腿就跑,同时也吵醒了熟睡的主人,这个场景中,“猫”就是被观察者,老鼠和人则是观察者。
3 ATM取钱
比如你到ATM机器上取钱,多次输错密码,卡就会被ATM吞掉,吞卡动作发生的时候,会触发哪些事件呢?第一,摄像头连续快拍,第二,通知监控系统,吞卡发生;第三,初始化ATM机屏幕,返回最初状态。一般前两个动作都是通过观察者模式来完成的,后一个动作是异常来完成。
4 广播收音机
电台在广播,你可以打开一个收音机,或者两个收音机来收听,电台就是被观察者,收音机就是观察者。