介绍
观察者模式(Observer Pattern)是一种行为设计模式。它允许一个对象(称为主题或可观察者)来监视并通知一组依赖于这个对象的其他对象(称为观察者),以便在主题状态发生变化时自动更新观察者的对象。
1.定义
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有注册过的观察者对象,使它们能够自动更新自己。
2.主要作用
主要作用是解耦主题与观察者之间的耦合关系,使得主题和观察者之间可以轻松地进行交互和通信。
3.解决的问题
-
对象间的依赖关系管理:将对象间的依赖关系从静态写死的调用关系解耦,通过抽象出观察者接口和主题接口来管理对象间的关系。
-
通知机制:当一个对象的状态发生变化时,需要通知其他相关的对象做出相应的改变,观察者模式提供了一种灵活的通知机制。
观察者模式应该是我们在开发中使用最频繁的一个设计模式了,比如在安卓中经常用到的:
- LiveData 和 Observer:LiveData 是一种在 Android 应用中生命周期感知的可观察数据持有类。可以存储数据,并在数据发生变化时通知其观察者。通常与 Observer 结合使用。
- BroadcastReceiver 和 IntentFilter:广播发送者和接收者之间的通信机制可以看作是一种观察者模式的实现。
- RxJava 和 Observer:提供了 Observables 和 Observers 的概念,可以用来实现观察者模式。
注意 观察者模式的定义是一对多的关系,像监听事件只能算回调(Callback),不严格符合传统的观察者模式的定义!
4.模式原理
包含角色:
-
Subject(主题):维护一组观察者对象,提供注册和删除观察者对象的接口。主题对象状态发生变化时,调用观察者的相应方法。
-
Observer(观察者):定义一个更新接口,使得在主题对象状态改变时能够得到通知和更新。
-
ConcreteSubject(具体主题):将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
-
ConcreteObserver(具体观察者):维护一个指向具体主题对象的引用,存储与主题的状态相对应的信息。实现 Observer 定义的更新接口,以便使自身状态与主题的状态保持一致。
UML类图:
示例代码:
主题接口 Subject
import java.util.ArrayList;
import java.util.List;
// 主题接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 具体主题类
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyObservers();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
}
观察者接口 Observer
// 观察者接口
interface Observer {
void update(int state);
}
// 具体观察者类
class ConcreteObserver implements Observer {
private int observerState;
@Override
public void update(int state) {
observerState = state;
System.out.println("Observer state updated: " + observerState);
}
}
使用
public class ObserverPatternExample {
public static void main(String[] args) {
// 创建具体主题对象
ConcreteSubject subject = new ConcreteSubject();
// 创建具体观察者对象
ConcreteObserver observer1 = new ConcreteObserver();
ConcreteObserver observer2 = new ConcreteObserver();
// 注册观察者
subject.registerObserver(observer1);
subject.registerObserver(observer2);
// 改变主题状态,并触发通知
subject.setState(10);
// 移除一个观察者
subject.removeObserver(observer1);
// 再次改变主题状态,并触发通知
subject.setState(20);
}
}
打印
Observer state updated: 10
Observer state updated: 10
Observer state updated: 20
上面举例为标准的观察者模式写法,我记得在上家公司代码中 有一种奇怪的写法,当然,也符合观察者模式定义。
在安卓中不同组件之间大多都有传递消息的需求,大家常用的库有EventBus
吧。简单,方便,它是定义了一个 IHandler
类,其中代码 就像上面 ConcreteSubject
类中差不多,只不过多了一个Handler
就是为了方便切换线程的,保证观察者收到的消息都是在主线程中,注意他是一个类,不是接口,而且所有代码都在IHandler
类中,谁继承它 就是 具体主题类 ,因为安卓是单继承嘛,这个使用起来很不方便。
无论你在任何地方,想要得到这个 具体主题类的消息,只需要向它注册观察者即可,当然最后别忘了反注册,不然要内存泄漏了,刚用的时候稍不留意就忘了,可恶心,当然 这个具体主题类 一般都是单例,全局唯一对象,因为当时是霸屏应用(特殊场景),所以单例满天飞,用的时候可爽,哈哈哈,现在回想起来都是问题。
5.优缺点
优点:
- 解耦性:主题和观察者之间是抽象耦合,容易扩展。
- 支持广播通信:主题可以通知多个观察者,观察者也可以接收多个主题的通知。
- 灵活性:可以动态地设置通知方式和观察者的添加和删除。
缺点:
- 可能引起循环调用:如果观察者和主题之间的依赖关系设计不当,可能导致循环调用。
- 通知效率:如果通知过于频繁,可能会影响系统性能,一般场景这个问题不用考虑的,但如果你的观察者好多个甚至几十上百的话 就不得不考虑效率问题了。
6.应用场景
- 当一个抽象模型有两个方面,其中一个依赖于另一个。
- 当一个对象的改变需要同时改变其他对象,而不知道具体有多少对象需要改变时。
- 当一个对象必须通知其他对象,而它又不能假定其他对象是谁。
7.总结
观察者模式通过定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,实现了对象之间的解耦和通信。它是实现事件驱动程序设计的基础,广泛应用于GUI(Graphical User Interface,图形用户界面)开发、事件处理系统等需要高内聚、低耦合的场景中。