设计模式系列文章目录导读:
设计模式 ~ 面向对象 6 大设计原则剖析与实战
设计模式 ~ 模板方法模式分析与实战
设计模式 ~ 观察者模式分析与实战
设计模式 ~ 单例模式分析与实战
设计模式 ~ 深入理解建造者模式与实战
设计模式 ~ 工厂模式剖析与实战
设计模式 ~ 适配器模式分析与实战
设计模式 ~ 装饰模式探究
设计模式 ~ 深入理解代理模式
设计模式 ~ 小结
今天我们继续来探讨下设计模式中的 观察者模式
,在很多开源框架中也有观察者模式的应用,如 RxJava
, EventBus
除了一些开源框架,Android Framework
源码中也用到了观察者模式,如 ListView/RecyclerView
, BroadcastReceiver
所以说掌握观察者模式是很有必要的,下面开始介绍观察者模式的原理以及在项目的一些实践
观察者模式基本概念
观察者模式(Observer Pattern
) 也叫做发布订阅模式(Publish/subscribe
)
观察者模式用于定义对象间一对多的依赖关系,使得每当一个对象改变状态,所有依赖于它的对象都会得到通知并被启动更新
观察者模式主要由四个角色组成:
-
抽象主题(Subject)
也叫做
被观察
者,可以增加和删除观察者对象 -
抽象观察者(Observer)
观察者收到主题的通知后,进行更新操作,对接收到的信息进行处理
-
具体主题(Concrete Subject)
不同的主题(被观察者)业务逻辑不一样,在具体的主题中定义自己特定的业务逻辑,同时定义对哪些事件进行通知
-
具体观察者(Concrete Observer)
不同类型的观察者接收到消息后处理逻辑不一样,根据需求定义具体观察者
观察者模式模式的类图如下所示:
观察者模式的实现与使用场景
通过上面的介绍,我们可以很轻松的实现一个观察者模式:
抽象主题(被观察者)
public interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers();
}
抽象观察者
public interface Observer {
void update();
}
具体主题
public class ConcreteSubject implements Subject {
private Vector<Observer> list = new Vector<>();
@Override
public void attach(Observer observer) {
list.add(observer);
}
@Override
public void detach(Observer observer) {
list.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer o : list) {
o.update();
}
}
// 模拟业务逻辑
public void doSomething(){
// 通知观察者
notifyObservers();
}
}
具体观察者
public class ConcreteObserver implements Observer {
@Override
public void update() {
System.out.println("收到通知,处理相关逻辑...");
}
}
测试类
public class Client {
public static void main(String[]args){
ConcreteSubject subject = new ConcreteSubject();
subject.attach(new ConcreteObserver());
subject.doSomething();
}
}
控制台输出:
收到通知,处理相关逻辑...
观察者模式实现起来还是比较简单的,主要是理解解决问题的思路,那么观察者模式主要是为了解决哪些方面的问题呢?
- 一个对象行为的改变,导致一个或多个对象的行为改变
- 事件的多级触发场景,例如 A 对象的行为影响 B 对象的行为,B 对象的行为影响 C 对象的行为
一般来说如果一个对象的行为依赖另一个对象的行为,那么当被依赖的对象的行为发生改变的时候去通知另一个对象就可以了
但是有的时候我们不想直接和另一个对象发生耦合,或者根本就拿不到那个对象,从而无法通知它。
这个时候使用 观察者模式就能很好的解耦了,因为观察者和被观察者是抽象耦合,被观察者不认识任何一个具体的观察者。
JDK 中的观察者模式
正式由于观察者模式在开发中使用的非常普遍,JDK
已经为我们提供了观察者模式的相关功能
下面我们来看下 JDK
是如何来实现观察者模式的,我们能从中学到什么?
主题(被观察者)
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(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 interface Observer {
void update(Observable o, Object arg);
}
JDK
实现的观察者模式和我们上面的实现的有些细节上的差别:
-
java.util.Observable
就是一个抽象主题,它并不是抽象类也不是接口这样大大简化了我们使用观察模式,我们的主题只需要继承
Observable
即可,但是这样就不能继承其他类了 -
通过
changed
标记来是判断是否通知观察者 -
如果正在通知观察者们,新添加的观察者将不会收到通知
-
通知观察者的时候可以传递参数
-
主题添加观察者的时候防止同一主题重复添加
下面来看下如何使用 JDK
提供的观察者模式:
// 具体主题
public class ConcreteSubject2 extends Observable {
// 模拟业务逻辑
public void doSomething(){
// changed = true
setChanged();
// 通知观察者
notifyObservers();
}
}
// 具体观察者
public class ConcreteObserver2 implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("收到通知,处理相关逻辑...");
}
}
// 测试类
public class Client2 {
public static void main(String[] args) {
ConcreteSubject2 subject = new ConcreteSubject2();
subject.addObserver(new ConcreteObserver2());
subject.doSomething();
}
}
控制台输出:
收到通知,处理相关逻辑...
观察者模式的缺点
虽然观察者模式应用的非常广泛,但是观察者模式也存在一些缺点:
-
如果一个主题对应过多的观察者,通知效率低
如果一个主题有多个直接或间接的观察者(订阅者众多),通知观察者需要花费的时间也就越多,会影响通知的效率,可以考虑通过异步的方式来解决,异步处理需要考虑线程安全和队列的问题
-
主题的循环依赖,导致程序崩溃
如果主题之间有循环依赖,这样就会触发它们之间的循环调用,导致系统崩溃,使用的时候需要特别注意
-
滥用观察者模式,可能导致程序不好维护
在
Android
开发中,使用的最多的观察者模式的框架就是EventBus
了,但是这也给我们带了很多问题,如果一个应用中任何地方都可以发通知,开发者并不知道某一个页面,会有多少个地方会通知它去修改,也不知道发送一个通知,会影响多少个页面。如果通知满天飞,使得程序的行为变得不可控,变得不好维护。虽然EventBus
的使用非常简单,但是如果一个程序滥用EventBus
,也会给程序带来非常多的负面影响
项目实践
关于观察者模式在项目中的实践就太多了,在 Android
开发中 EventBus
用的也比较多,使得我们在使用观察者模式更加简单
Android
开发中需要用到的观察者模式的地方太多,所以才使得 EventBus
的使用非常普遍,所以在这里就不通过具体的案例来分析观察者模式的使用了,常见的场景比如:
- 推送来了,需要刷新页面
- 多个页面依赖某个状态的变化
总而言之,某个对象的发生变化,需要通知其他对象,但是又拿不到其他对象的引用,或者不适合直接强耦合;抑或是一个对象行为的改变,导致一个或多个对象的行为改变,都可以使用 观察者模式
Reference
- 《设计模式之禅》
- 《Java设计模式及实践》
- 《Java设计模式深入研究》
- 《设计模式(Java版)》
如果你觉得本文帮助到你,给我个关注和赞呗!
另外,我为 Android 程序员编写了一份:超详细的 Android 程序员所需要的技术栈思维导图。
如果有需要可以移步我的 GitHub -> AndroidAll,里面包含了最全的目录和对应知识点链接,帮你扫除 Android 知识点盲区。 由于篇幅原因只展示了 Android 思维导图: