初识设计模式 - 观察者模式
观察者设计模式(Observer Design Pattern)的别名有很多,如发布 - 订阅(Publish/Subscribe)模式、模型 - 视图(Model/View)模式、源 - 监听(Source/Listener)模式或从属者(Dependents)模式。无论是何种名称,其意图都是在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。其主要解决了一
🚀 优质资源分享 🚀
学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
🧡 Python实战微信订餐小程序 🧡 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
💛Python量化交易实战💛 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
简介
观察者设计模式(Observer Design Pattern)的别名有很多,如发布 - 订阅(Publish/Subscribe)模式、模型 - 视图(Model/View)模式、源 - 监听(Source/Listener)模式或从属者(Dependents)模式。
无论是何种名称,其意图都是在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。
其主要解决了一个对象状态改变之后给其他对象通知的问题,而且考虑到易用性和低耦合,保证高度的协作性。
典型实现
在发布 - 订阅模式当中,观察者就是订阅者,一般是需要定义一个抽象的观察者接口,其代码示例如下:
| | public interface Observer { |
| | void update(); |
| | } |
具体的观察者类也很简单,只需要简单实现接口即可,其代码示例如下:
| | public class ConcreteObserver implements Observer { |
| | // 实现响应方法 |
| | public void update() { |
| | // 具体响应代码 |
| | } |
| | } |
定义完订阅者,其次就是发布者,通常是将之定义为一个抽象的目标类,其代码示例如下:
| | public abstract class Subject { |
| | // 定义一个观察者集合存储所有观察者对象 |
| | protected List observers = new ArrayList<>(); |
| | |
| | // 注册方法,用于向观察者集合增加一个观察者 |
| | public void attach(Observer observer) { |
| | observers.add(observer); |
| | } |
| | |
| | // 注销方法,用于向观察者集合删除一个观察者 |
| | public void detach(Observer observer) { |
| | observers.remove(observer); |
| | } |
| | |
| | // 声明抽象的通知方法,需要由子类具体实现 |
| | abstract void notifyObservers(); |
| | } |
根据不同的场景定义不一样的具体目标类,如下是一种代码示例:
| | public class ConcreteSubject extends Subject { |
| | // 实现具体的通知方法 |
| | @Override |
| | public void notifyObservers() { |
| | // 遍历观察者集合,调用每一个观察者的响应方法 |
| | for (Observer ob : observers) { |
| | ob.update(); |
| | } |
| | } |
| | } |
总结
优点
观察者模式的主要优点如下:
- 观察者模式可以实现表示层和数据逻辑层的分离
- 观察者模式在观察目标和观察者之间建立了一个抽象的耦合
- 观察者模式支持广播通信,简化了一对多系统设计的难度
- 增加新的具体观察者无需修改原有代码,符合开闭原则
缺点
观察者模式的主要缺点如下:
- 如果目标对象的观察者有很多,将所有的观察者都通知到会非常耗时
- 如果目标对象和观察者存在循环依赖,观察目标会触发它们之间进行循环调用,最终导致系统崩溃
适用场景
观察者模式的适用场景如下:
- 将具有依赖关系的两种抽象模型独立出来,使它们可以独立地改变和复用
- 一个对象的改变将导致一个或多个其他对象也发生改变,但并不知道具体有多少对象将发生改变,也不知道这些对象是谁
- 可以使用观察者模式创建一种链式触发机制
源码
在 JDK 的 java.util
包中,提供了 Observer
接口和 Observable
类,它们构成了 JDK 对观察者模式的支持。
如下是 Observer
接口的源码:
| | @Deprecated(since = "9") |
| | public interface Observer { |
| | void update(Observable o, Object arg); |
| | } |
如下是 Observable
类的部分源码:
| | @Deprecated(since = "9") |
| | public class Observable { |
| | private boolean changed = false; |
| | private Vector obs; |
| | |
| | public Observable() { |
| | obs = new Vector<>(); |
| | } |
| | |
| | // 注册观察者,线程安全 |
| | public synchronized void addObserver(Observer o) { |
| | if (o == null) |
| | throw new NullPointerException(); |
| | // 注册时去重 |
| | if (!obs.contains(o)) { |
| | obs.addElement(o); |
| | } |
| | } |
| | |
| | // 注销观察者,线程安全 |
| | public synchronized void deleteObserver(Observer o) { |
| | obs.removeElement(o); |
| | } |
| | |
| | // 通知观察者,无参数模式 |
| | public void notifyObservers() { |
| | notifyObservers(null); |
| | } |
| | |
| | // 通知观察者,带参数模式 |
| | public void notifyObservers(Object arg) { |
| | // 保存观察者的状态,备忘录模式的简单应用 |
| | Object[] arrLocal; |
| | |
| | // 获取观察者时候锁住 |
| | synchronized (this) { |
| | if (!changed) |
| | return; |
| | arrLocal = obs.toArray(); |
| | clearChanged(); |
| | } |
| | |
| | // 逐一调用观察者的处理方法 |
| | for (int i = arrLocal.length - 1; i >= 0; i--) |
| | ((Observer) arrLocal[i]).update(this, arg); |
| | } |
| | |
| | public synchronized void deleteObservers() { |
| | obs.removeAllElements(); |
| | } |
| | |
| | protected synchronized void setChanged() { |
| | changed = true; |
| | } |
| | |
| | protected synchronized void clearChanged() { |
| | changed = false; |
| | } |
| | |
| | public synchronized boolean hasChanged() { |
| | return changed; |
| | } |
| | |
| | public synchronized int countObservers() { |
| | return obs.size(); |
| | } |
| | } |
需要注意的是,在 JDK 9 之后已经不推荐使用 Observer
接口和 Observable
类。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)