在开发过程中,我们经常会碰到这样的需求:如果一个对象A的状态发生改变,某些与它相关的对象也要随之做出相应的变化。通常来说,需要对象 A 能访问到这些对象,从而去调用更新方法,但是这是一种不受欢迎的会造成耦合的方式。为了低耦合,可以采用观察者模式。
定义
Observer 观察者模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在更改其状态时,会通知所有的观察者对象,使他们能够自动更新自己。一个主题对象可以对应多个观察者,而这些观察者之前没有相互联系,可以根据需要增加和删除观察者,使得系统更易于拓展。
结构
- Subject:抽象主题。抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
- ConcreteSubject:具体主题。该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有订阅过的观察者发送通知。
- Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
- ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。
简单实现
- 抽象主题类
abstract class Subject {
private Vector obs = new Vector();
public void addObserver(Observer obs){
this.obs.add(obs);
}
public void delObserver(Observer obs){
this.obs.remove(obs);
}
protected void notifyObserver(){
for(Observer o: obs){
o.update();
}
}
public abstract void doSomething();
}- 具体主题类
class ConcreteSubject extends Subject {
public void doSomething(){
System.out.println("被观察者事件反生");
this.notifyObserver();
}
}- 抽象观察者
interface Observer {
public void update();
}- 具体观察者
class ConcreteObserver1 implements Observer {
public void update() {
System.out.println("观察者1收到信息,并进行处理。");
}
}
class ConcreteObserver2 implements Observer {
public void update() {
System.out.println("观察者2收到信息,并进行处理。");
}
}- 使用
public class Client {
public static void main(String[] args){
Subject sub = new ConcreteSubject();
sub.addObserver(new ConcreteObserver1()); //添加观察者1
sub.addObserver(new ConcreteObserver2()); //添加观察者2
sub.doSomething();
}
}运行结果:
被观察者事件反生
观察者1收到信息,并进行处理。
观察者2收到信息,并进行处理。
实例
NSNotificationCenter 和 NSNotification
iOS 中的通知机制是非常典型的观察者模式,所有的类可以通过 NSNotificationCenter 监听和发送 NSNotification,观察者和被观察者都无需知晓对方,只需要通过标记(例如 NotificationName)在 NSNotificationCenter 中找到监听该通知所对应的类,从而调用该类的方法。并且,在 NSNotificationCenter 中,观察者可以只订阅某一特定的通知,并对其做出响应,而不用对某一个类发送的所有通知都进行更新操作。NSNotificationCenter 对观察者的调用不是随机的,而是遵循注册顺序一一执行,并且在该线程内是同步的。
@property (class, readonly, strong) NSNotificationCenter *defaultCenter;
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;
- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(nullable NSNotificationName)aName object:(nullable id)anObjec优缺点
优点
1. 解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。
2. 观察者模式符合“开闭原则”的要求,若需要引入新的观察者,无需对被观察对象进行修改
缺点
1. 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间,同时,开发和调试的复杂程度提高
2. 如果在观察者和观察目标之间有循环依赖,有可能会进行循环调用,导致系统崩溃
和其他设计模式的对比
责任链模式
观察者模式也可以将通知链式分就会发,与责任链模式十分相似
1. 责任链模式中,每个接收者都包含对另一个接收者的引用,而观察者模式中,被观察者并不知晓观察者具体是谁
2. 在纯责任链模式中,只有一个接收者会对请求进行处理,观察者模式允许每个观察者都进行处理
命令模式
1. 命令模式是一对一的关系,观察者模式是一对多的关系
2. 命令模式是通过发送命令让接收方来执行,而观察者模式中发出消息的类(被观察者)并不关心接收消息的类(观察者)会如何处理
中介者模式
中介者模式各个组件之间通过中介建立的是双向连接,而观察者模式的被观察者和观察者之间是单向的。
中模式与观察者模式难以被区分的原因之一是中介者模式的一个流行实现依赖于观察者模式。中介对象扮演发布者的角色,组件充当观察者,订阅和取消订阅中介的事件。当以这种方式实现中介者模式时,它可能看起来非常类似于观察者模式。但是,中介者模式还可以用其他方式实现。例如,可以将所有组件永久链接到同一个中介对象。这个实现不像观察者模式,但仍然是中介者模式。