观察者模式

观察者模式

一、定义

摘自百度百科: 观察者模式(有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式或从属者模式)是如那件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

点击查看源码


二、角色分类

目标角色(Subject)

被观察者,其为被观察的对象,可以是接口、抽象类或具体的类,但是因为很多情况下容易和其他模式混合使用,所以一般使用抽象类的情况多一些

具体目标角色(Concrete Subject)

其为目标角色的子类,通常它会包括经常发生改变的数据,当它的状态发生改变时会向它的各个观察者发送通知。同时它还可以实现在目标类中定义的抽象业务逻辑方法,如果无须扩展目标类,则具体目标类可以省略

抽象观察者角色(Observer)

观察目标,并对观察目标的改变做出反应,一般将其定义为接口,声明了更新数据的方法

具体观察者(Concrete Observer)

在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致;它实现了在抽象观察者 中定义的更新数据的方法

客户角色(Client)

具体调用方法的角色


三、实现方式

UML图

未命名文件

具体实现

观察者角色(Observer)

1
2
3
public interface Observer {
public void update();
}

目标角色(Subject)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public abstract class Subject {
private Vector<Observer> obs = new Vector<>();

/**
* 添加观察者
*/
public void addObserver(Observer obs) {
this.obs.add(obs);
}

/**
* 移除观察者
*/
public void delObserver(Observer obs) {
this.obs.remove(obs);
}

/**
* 通知观察者
*/
protected void notifyObserver() {
obs.forEach(any -> any.update());
}

/**
* 其他业务
*/
public abstract void doSomething();

}

具体目标角色(Concrete Subject)

1
2
3
4
5
6
public class ConcreteSubject extends Subject {
public void doSomething() {
System.out.println("目标角色发生改变");
this.notifyObserver();
}
}

具体观察者角色(Concrete Observer)

1
2
3
4
5
6
7
8
9
10
11
public class ConcreteObserverA implements Observer {
public void update() {
System.out.println("观察者A收到目标角色改变事件");
}
}

public class ConcreteObserverB implements Observer {
public void update() {
System.out.println("观察者B收到目标角色改变事件");
}
}

客户角色(Client)

1
2
3
4
5
6
7
8
public class Client {
public static void main() {
Subject sub = new ConcreteSubject();
sub.addObserver(new ConcreteObserverA());
sub.addObserver(new ConcreteObserverB());
sub.doSomething();
}
}

运行结果

1
2
3
目标角色发生改变
观察者A收到目标角色改变事件
观察者B收到目标角色改变事件

四、应用场景

以下部分内容摘自菜鸟教程

意图: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

主要解决: 一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

何时使用: 一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

如何解决: 使用面向对象技术,可以将这种依赖关系弱化。

关键代码: 在抽象类里有一个 ArrayList 或 Vector 存放观察者们。

应用实例:

  1. 拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。
  2. 西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。

使用场景:

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

注意事项:

  1. JAVA 中已经有了对观察者模式的支持类。
  2. 避免循环引用。
  3. 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

五、优缺点

优点

  1. 观察者和被观察者是抽象耦合的。
  2. 建立一套触发机制。

缺点

  1. 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  2. 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  3. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。