友情支持

如果您觉得这个笔记对您有所帮助,看在D瓜哥码这么多字的辛苦上,请友情支持一下,D瓜哥感激不尽,😜

支付宝

微信

有些打赏的朋友希望可以加个好友,欢迎关注D 瓜哥的微信公众号,这样就可以通过公众号的回复直接给我发信息。

wx jikerizhi

公众号的微信号是: jikerizhi因为众所周知的原因,有时图片加载不出来。 如果图片加载不出来可以直接通过搜索微信号来查找我的公众号。

20. 观察者模式

20.1. 定义

根据 GoF 的著名著作 《设计模式》,观察者模式的定义如下:

观察者模式(Observer)

定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

— Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides
《设计模式》

20.2. 类图

Diagram

观察者模式 注: JDK中内置对观察者模式的支持,查看一下具体实现。以及如何应用案例。

观察者模式又叫做发布-订阅(Publish/Subscribe)模式。

Subject类,可翻译为主题或抽象通知者,一般用一个抽象类或者一个接口实现。它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

Observer类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者一般用一个抽象类或者一个接口实现。更新接口通常包含一个Update()方法,这个方法叫做更新方法。

ConcreteSubject类,叫做具体主题或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。

ConcreteObserver类,具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。具体观察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。

用观察者模式的动机是什么呢?

将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便[DP]。而观察者模式的关键对象是主题Subject和观察者Observer,一个Subject可以有任意数目的依赖它的Observer,一旦Subject的状态发生了改变,所有的Observer都可以得到通知。Subject发出通知时并不需要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。而任何一个具体观察者不知道也不需要知道其他观察者的存在。

什么时候考虑使用观察者模式呢?

当一个对象的改变需要同时改变其他对象的时候。

而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。

当一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。” “非常好,总的来讲,观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。

事件委托实现 注: Java中如何实现委托?

“抽象通知者”由于不希望依赖“抽象观察者”,所以“增加”和“减少”的方法也就没有必要了(抽象观察者已经不存在了)。

委托就是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。委托可以看作是对函数的抽象,是函数的‘类’,委托的实例将代表一个具体的函数。

一旦为委托分配了方法,委托将与该方法具有完全相同的行为。而且,一个委托可以搭载多个方法,所有方法被依次唤起。更重要的是,它可以使得委托对象所搭载的方法并不需要属于同一个类。

委托对象所搭载的所有方法必须具有相同的原形和形式,也就是拥有相同的参数列表和返回值类型。

代码 118. observer/ConcreteObserver.java 类
 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
31
32
package com.diguage.didp.observer;

/**
 * ConcreteObserver 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-19 17:53:35
 */
public class ConcreteObserver implements Observer {
  private String name;
  private String observerState;
  private ConcreteSubject subject;

  public ConcreteObserver(ConcreteSubject subject, String name) {
    this.name = name;
    this.subject = subject;
  }

  @Override
  public void update() {
    observerState = subject.getSubjectState();
    System.out.printf("观察者 %s 的新状态是 %s%n", name, observerState);
  }

  public ConcreteSubject getSubject() {
    return subject;
  }

  public void setSubject(ConcreteSubject subject) {
    this.subject = subject;
  }
}
代码 119. observer/ConcreteSubject.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.diguage.didp.observer;

/**
 * ConcreteSubject 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-23 10:18:27
 */
public class ConcreteSubject extends Subject {
  private String subjectState;

  public String getSubjectState() {
    return subjectState;
  }

  public void setSubjectState(String subjectState) {
    this.subjectState = subjectState;
  }
}
代码 120. observer/Observer.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.diguage.didp.observer;

/**
 * Observer 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-19 17:53:35
 */
public interface Observer {
  void update();
}
代码 121. observer/Subject.java 类
 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
package com.diguage.didp.observer;

import java.util.ArrayList;
import java.util.List;

/**
 * Subject 类
 *
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-19 17:53:35
 */
public abstract class Subject {
  private List<Observer> observers = new ArrayList<>();

  public void attach(Observer observer) {
    observers.add(observer);
  }

  public void detach(Observer observer) {
    observers.remove(observer);
  }

  public void doNotify() {
    observers.forEach(Observer::update);
  }
}
代码 122. observer/Client.java 类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package com.diguage.didp.observer;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2017-05-25 21:19:03
 */
public class Client {
  public static void main(String[] args) {
    ConcreteSubject subject = new ConcreteSubject();
    subject.attach(new ConcreteObserver(subject, "X"));
    subject.attach(new ConcreteObserver(subject, "Y"));
    subject.attach(new ConcreteObserver(subject, "Z"));
    subject.setSubjectState("ABC");
    subject.doNotify();
  }
}

20.3. 常见示例

  1. 观察者模式的使用案例

  2. 观察者模式的定义以及实力代码

  3. JDK 对观察者模式的支持 java.util.EventListener

  4. 在 Spring 中使用观察者模式来实现事件发布

  5. jakarta.servlet.http.HttpSessionBindingListener

  6. jakarta.servlet.http.HttpSessionAttributeListener