公号:码农充电站pro
主页:https://codeshellme.github.io
观察者模式(Observer Design Pattern)也被称为发布订阅模式(Publish-Subscribe Design Pattern),主要用于更好的解决向对象通知消息的问题。
观察者模式定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
观察者模式可以用很多称呼来描述,比如:
1,订阅报纸
我们以订阅报纸为例,来描述 Subject 与 Observer 之间的关系。
Subject 相当于报社,Observer 就相当于订阅报纸的用户:
- 从报社的角度来说:
- 从用户的角度来说:
Subject 与 Observer 是一对多关系:
2,观察者模式类图
这里直接给出观察者模式的类图,这是最经典的实现方式,其它的变种都可以在它的基础上加以改进。
从类图中可以知道,Subject 是一个接口,有三个抽象方法:
Observer 也是一个接口,有一个抽象方法:
-
update
:当 subject 发来新消息时,用于更新消息。
3,实现观察者模式
下面我们来用代码实现观察者模式。
首先是两个接口 Subject 与 Observer:
interface Subject { void registerObserver(Observer o); void removeObserver(Observer o); void notifyObservers(String info); } interface Observer { void update(String info); }
@H_301_176@这两个接口完全是按照类图中的内容来实现的,其中变量
info
的类型可以根据实际的应用场景来定。再实现 ConcreteSubject 和 ConcreteObserver :
class ConcreteSubject implements Subject { // 用于存放 observer private final ArrayList<Observer> observers; public ConcreteSubject() { observers = new ArrayList(); } public void registerObserver(Observer o) { observers.add(o); } public void removeObserver(Observer o) { observers.remove(o); } public void notifyObservers(String info) { for (Observer o: observers) { o.update(info); } } } class ConcreteObserver implements Observer { private final String name; public ConcreteObserver(String name) { this.name = name; } public void update(String info) { System.out.println(this.name + " get info: " + info); } }
@H_301_176@
ConcreteSubject
中的observers
用于存储观察者,这里使用的类型是ArrayList
,也可以根据实际的应用场景来选择。
ConcreteObserver
中的name
只是为了表示不同的观察者,观察者在收到消息后,将消息打印在控制台。测试这两个类:
// 创建被观察者 ConcreteSubject s = new ConcreteSubject(); // 创建两个观察者 ConcreteObserver o1 = new ConcreteObserver("o1"); ConcreteObserver o2 = new ConcreteObserver("o2"); // 注册观察者 s.registerObserver(o1); // 注册 o1 s.registerObserver(o2); // 注册 o2 s.notifyObservers("info1"); // 向观察者通知消息 System.out.println("remove observer o1"); s.removeObserver(o1); // 移除 o1 s.notifyObservers("info2"); // 再向观察者通知消息
@H_301_176@输出如下:
o1 get info: info1 o2 get info: info1 remove observer o1 o2 get info: info2
@H_301_176@可以看到,第一次通知消息时,o1 和 o2 都收到消息了,在移除 o1 之后再发送消息,只有 o2 能收到消息。
这就是观察者模式最简洁的一种实现方式,非常简单。我把完整的代码放在了这里。
4,观察者模式扩展
根据不同的应用场景和需求,观察者模式可以有不同的实现方式,比如下面几种:
- 同步阻塞的实现方式
- 异步非阻塞的实现方式
- 进程内的实现方式
- 跨进程的实现方式
同步阻塞方式
根据这种划分方式,上面我们实现的就是同步阻塞的方式,当有新消息的时候,Subject
会将消息 notify
给所有的 Observer
,直到所有的 Observer
执行完毕它的 update
过程,整个通知过程才算完毕,这整个过程是一个阻塞的过程。
异步非阻塞方式
为了加快整个 notify
过程,我们可以将同步阻塞的方式改为异步非阻塞的方式。
一种简单的实现就是使用线程,就是在 update
方法中使用线程来完成任务,如下:
public void update(String info) { Thread t = new Thread(new Runnable() { public void run() { // 处理任务 } }); t.start(); }
@H_301_176@Google Guava EventBusExplained 是一个通用的观察者模式框架,你可以去了解一下。
跨进程方式
同步阻塞与异步非阻塞都属于进程之内的实现,对于跨进程的实现,一般都是基于消息队列来实现。至于这方面的应用,有很多现成的,成熟的组件可以使用,比如:
- Redis Pub/Sub:一个快速、稳定的发布/订阅消息传递系统。
- ActiveMQ:一个基于 Java 的多协议的消息传递服务。
- RocketMQ:一个消息传递引擎。
- Kafka:一个分布式的大数据流处理平台。
5,总结
观察者模式旨在将观察者与被观察者解耦,在很多地方都用到了该模式,比如 Swing,JavaBeans 等。
观察者模式最经典的实现方式很简单,在实际应用中,可以在其基础上进行改进。
(本节完。)
推荐阅读:
欢迎关注作者公众号,获取更多技术干货。