设计模式-观察者模式(observer)

    设计模式-观察者模式(observer)

 

   概要 

   记忆关键词:通知、自动更新 

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

   分析:观察者模式又叫做发布-订阅(Publish/Subscribe)模式。观察者模式所做的工作其实就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。这其实是依赖倒置原则的最佳体现。 

   类型:行为型 

   观察者模式结构图:

     

   一、能解决什么问题

   观察者模式适用于需要事件通知的场景。通过使用观察者模式,当某个事件发生时,主题对象会通知所有注册的观察者,观察者可以根据需要对事件做出反应。这种机制广泛应用于 GUI 框架、事件驱动编程和实时系统中。

   二、涉及的相关角色

   1. Subject  被观察者

 1 abstract class Subject {
 2     private List<Observer> obs = new ArrayList<>();
 3 
 4     public void addObserver(Observer obs){
 5         this.obs.add(obs);
 6     }
 7     public void delObserver(Observer obs){
 8         this.obs.remove(obs);
 9     }
10     protected void notifyObserver(){
11         for(Observer o: obs){
12             o.update();
13         }
14     }
15     public abstract void doSomething();
16 }

    2. ConcreteSubject  具体被观察者

1 class ConcreteSubject extends Subject {
2     public void doSomething(){
3         System.out.println("被观察者事件发生改变");
4         this.notifyObserver();
5     }
6 }

   3. Observer类 观察者

1 interface Observer {
4     public void update();
8 }

   4. Concreteobserver类  具体观察者

 1 class ConcreteObserver1 implements Observer {
 2     public void update() {
 3         System.out.println("观察者1收到信息,并进行处理");
 4     }
 5 }
 6 class ConcreteObserver2 implements Observer {
 7     public void update() {
 8         System.out.println("观察者2收到信息,并进行处理");
 9     }
10 }

   5. 客户端

 1 public class Client {
 2     public static void main(String[] args){
 3         Subject sub = new ConcreteSubject();
 4         sub.addObserver(new ConcreteObserver1()); //添加观察者1
 5         sub.addObserver(new ConcreteObserver2()); //添加观察者2
 6         sub.doSomething();
 7     }
 8 }
 9 
10 //运行结果
11 被观察者事件发生改变
12 观察者1收到信息,并进行处理
13 观察者2收到信息,并进行处理

   三、推模型和拉模型

   1. 推模型

   在推模型中,当主题(Subject)发生变化时,它会主动将更新的数据或信息推送给所有的观察者(Observer)。每个观察者在接收到通知时都能直接获得更新的信息或数据。

   特点:

  • 主动推送:主题主动推送更新给观察者。

  • 简化观察者逻辑:观察者不需要请求数据,数据已由主题提供。

  • 适用于数据量较小或更新频率较低的场景:由于每次通知都会推送完整的数据或信息,适合数据量小且更新频率不高的场景。

    代码示例:

 1 interface Observer {
 2     void update(String data);
 3 }
 4 
 5 class ConcreteSubject {
 6     private List<Observer> observers = new ArrayList<>();
 7     private String data;
 8 
 9     public void registerObserver(Observer observer) {
10         observers.add(observer);
11     }
12 
13     public void notifyObservers() {
14         for (Observer observer : observers) {
15             observer.update(data);  // Push model: provide the data directly
16         }
17     }
18 
19     public void setData(String data) {
20         this.data = data;
21         notifyObservers();
22     }
23 }

   2. 拉模型

   在拉模型中,当主题发生变化时,它会通知观察者有更新,但不直接提供数据。观察者需主动请求主题获取更新的数据或信息。观察者在接收到通知后,通常会调用主题的方法以获取最新的数据。

   特点:

  • 被动拉取:观察者主动拉取(请求)更新的数据。
  • 更灵活:观察者可以根据需要选择是否拉取数据,或者以不同的方式处理数据。
  • 适用于数据量较大或更新频率较高的场景:观察者只在需要时拉取数据,有助于减少不必要的数据传输。

   代码示例:

 1 interface Observer {
 2     void update(ConcreteSubject subject);
 3 }
 4 
 5 class ConcreteSubject {
 6     private List<Observer> observers = new ArrayList<>();
 7     private String data;
 8 
 9     public void registerObserver(Observer observer) {
10         observers.add(observer);
11     }
12 
13     public void notifyObservers() {
14         for (Observer observer : observers) {
15             observer.update(this);  // Pull model: notify observers to pull data
16         }
17     }
18 
19     public void setData(String data) {
20         this.data = data;
21         notifyObservers();
22     }
23 
24     public String getData() {
25         return data;
26     }
27 }
28 
29 class ConcreteObserver implements Observer {
30     @Override
31     public void update(ConcreteSubject subject) {
32         String data = subject.getData();  // Pull the data from the subject
33         System.out.println("Received data: " + data);
34     }
35 }

   四、什么时候使用观察者模式?

   1.  当一个对象的改变需要同时改变其他对象的时候,而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。

   2. 当一个抽象模型有两方面,其中一方面依赖另一方面,这时用观察者模式可以将这两者封装在独立的对象中,使它们各自独立地改变和复用。

   比如下面这些情况下:

   1)订单支付成功后,会做各种动作,比如发送EMAIL,或者改变订单状态,发送短信给客户,修改优惠券,通知仓库订单号等等

   2)网站登录之后,修改登录时间,推送最新活动,让附近的人给他打招呼

   五、应用场景 

   1.  JDK中的观察者模式

   观察者模式在 Java 语言中的地位非常重要。在 JDK 的 java.util 包中,提供了 Observable 类以及 Observer 接口,它们构成了 JDK 对观察者模式的支持。but,在 Java9 被弃用了。

   java.util.Observer接口

1 package java.util;
2 
3 public interface Observer {
4     void update(Observable o, Object arg);
5 }

   java.util.Observable类

 1 import java.util.Observer;
 2 import java.util.Vector;
 3 
 4 /**
 5  * 这里其实就是主题接口的角色,只不过 JDK 的实现很烂——竟然用类封装的,这是公认的槽点之一。
 6  */
 7 public class Observable {
 8     private boolean changed = false;
 9 
10     // 看到这里,其实也知道,这个API不仅太古老,而且还没人维护了,用的还是最老的,被淘汰的 Vector 实现的动态数组
11     private Vector obs;
12 
13     public Observable() {
14         obs = new Vector();
15     }
16 
17     // 注册观察者,线程安全,这是优点之一,可以借鉴
18     public synchronized void addObserver(Observer o) {
19         if (o == null) // 提高代码健壮性
20             throw new NullPointerException();
21         if (!obs.contains(o)) { // 注册时会去重,自定义实现需要注意也去重
22             obs.addElement(o);
23         }
24     }
25 
26     // 观察者取消注册
27     public synchronized void deleteObserver(Observer o) {
28         obs.removeElement(o);
29     }
30 
31     // 基于拉模型的通知方法
32     public void notifyObservers() {
33         notifyObservers(null);
34     }
35 
36     // 基于推模型
37     public void notifyObservers(Object arg) {
38         // 一个临时数组,用于并发访问被观察者时,保存观察者列表的当前状态——这就是基于备忘录模式的简单应用。
39         Object[] arrLocal;
40         // 在获取到观察者列表之前,不允许其他线程改变观察者列表
41         synchronized (this) {
42             if (!changed)
43                 return;
44             arrLocal = obs.toArray();
45             // 重置变化标记位为 false
46             clearChanged();
47         }
48 
49         // 主题类释放锁,但是并不影响线程安全,因为加锁之前已经将观察者列表复制到临时数组 arrLocal
50         // 在通知时我们只通知数组中的观察者,当前删除和添加观察者,都不会影响我们通知的对象
51         for (int i = arrLocal.length - 1; i >= 0; i--)
52             ((Observer) arrLocal[i]).update(this, arg);
53     }
54 
55     public synchronized void deleteObservers() {
56         obs.removeAllElements();
57     }
58 
59     protected synchronized void setChanged() {
60         changed = true;
61     }
62 
63     protected synchronized void clearChanged() {
64         changed = false;
65     }
66 
67     public synchronized boolean hasChanged() {
68         return changed;
69     }
70 
71     public synchronized int countObservers() {
72         return obs.size();
73     }
74 }

   2. Spring 中的观察者模式

   具体介绍请参考文章《Spring - 事件驱动模型》

   

 

   参考链接:

   https://www.cnblogs.com/study-hard-forever/p/13167161.html

   https://juejin.cn/post/6844904100459446285

posted @ 2020-05-28 13:55  欢乐豆123  阅读(443)  评论(0编辑  收藏  举报