初识设计模式 - 观察者模式
Posted csdn_manong1
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初识设计模式 - 观察者模式相关的知识,希望对你有一定的参考价值。
简介
观察者设计模式(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<Observer> 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<Observer> 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
类。
观察者模式 vs 发布-订阅模式
参考技术A 差异总结:1. 在观察者模式中,观察者 (Observer) 是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者 (Publisher) 和订阅者 (Subscriber) 不知道对方的存在。它们只有通过消息代理 ( 信息中介 ) 进行通信。
2. 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
3. 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。
4. 在观察者模式中依赖于Subject 对象的一系列 Observer 对象在被通知之后只能执行同一个特定的更新方法,而在发布订阅模式中则可以基于不同的主题去执行不同的自定义事件。
模型阐述:
观察者模式里,观察者(Observer)直接订阅(subscribe)主题(Subject),而当主题被激活的时候,会触发(fire)观察者里的事件。
订阅发布模式里,订阅者(Subscriber)通过监听(on)事件总线(Event Bus)里的事件,当事件总线里的事件被触发(emit)的时候,订阅者将会执行相应的操作。而这里需要注意的是,事件总线里的事件是通过发布者(Publisher)进行发布(publish)和 通知事件总线 触发 的。
以上是关于初识设计模式 - 观察者模式的主要内容,如果未能解决你的问题,请参考以下文章