Post

观察者模式

模式动机

建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将作出反应。发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。

模式定义

观察者模式(Observer Pattern): 定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相对依赖对象皆得到通知并被自动更新.观察者模式又被叫做发布-订阅(Publish/Subscribe)模式,模型-视图(Model/View)模式,源-监听器(Source/Listener)模式或从属者(Dependents)模式.观察者模式是一种对象行为型模式.

Observer Pattern: Define a one-to-many dependency between objects so that when one object changes state,all its dependents are notified and updated automatically.

模式结构

模式结构图

模式分析

观察者模式包含四个角色:目标又称为主题,它是指被观察的对象;具体目标是目标类的子类,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;观察者将对观察目标的改变做出反应;在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致。

观察者模式是一种松耦合: 两个对象可以交互,但是并不清楚彼此的细节,只需要遵守之间交互的接口

模式优缺点

  • 优点 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者对象

观察者模式在观察目标和观察者之间建立一个抽象的耦合

支持广播通信

符合”开闭原则”要求

  • 缺点

如果一个观察目标对象有很多直接和间接的观察者,将所有观察者都通知会花费很多的时间

如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间的循环调用,可能导致系统崩溃

观察者模式没有相应的机制让观察者指导所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化

模式使用环境

一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用

一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度

一个对象必须通知其他对象,而并不知道这些对象是谁

模式应用

  • Java语言提供对观察者模式的支持

方案一:在JDK的java.util包中,提供了Observable类以及Observer接口, 它们构成了Java语言对观察者模式的支持。(Java 9后标记为过时)

JavaObservable

案例

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.util.Observable;
import java.util.Observer;

// 主题(被观察者):新闻发布平台
class NewsPublisher extends Observable {
    private String news;

    public void publishNews(String news) {
        this.news = news;
        setChanged();       // 标记状态变化
        notifyObservers();  // 通知观察者
    }

    public String getNews() {
        return news;
    }
}

// 观察者:订阅用户
class Subscriber implements Observer {
    private String name;

    public Subscriber(String name) {
        this.name = name;
    }

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof NewsPublisher) {
            String news = ((NewsPublisher) o).getNews();
            System.out.println(name + " 收到新闻:" + news);
        }
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        NewsPublisher publisher = new NewsPublisher();
        Subscriber user1 = new Subscriber("Alice");
        Subscriber user2 = new Subscriber("Bob");

        publisher.addObserver(user1);
        publisher.addObserver(user2);

        publisher.publishNews("Java 21 正式发布!");
    }
}

局限性 Observable 是类而非接口,限制了继承灵活性。

无法传递详细事件数据(需通过 getNews() 主动拉取)。

方案二:Java 标准库中的 PropertyChangeListener 和 PropertyChangeSupport 也可用于观察者模式

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
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

class TemperatureSensor {
    private final PropertyChangeSupport support = new PropertyChangeSupport(this);
    private double temperature;

    public void addListener(PropertyChangeListener listener) {
        support.addPropertyChangeListener(listener);
    }

    public void setTemperature(double newTemp) {
        double oldTemp = this.temperature;
        this.temperature = newTemp;
        support.firePropertyChange("temperature", oldTemp, newTemp);
    }
}

// 观察者实现 PropertyChangeListener 接口
class HeatingSystem implements PropertyChangeListener {
    @Override
    public void propertyChange(java.beans.PropertyChangeEvent evt) {
        if ("temperature".equals(evt.getPropertyName())) {
            double newTemp = (Double) evt.getNewValue();
            if (newTemp < 18.0) {
                System.out.println("供暖系统启动!");
            }
        }
    }
}
方案使用场景优点缺点
Observable/Observer简单通知场景快速实现已弃用,功能有限
自定义接口+事件对象复杂业务逻辑灵活、可扩展需手动实现观察者管理
PropertyChangeSupport属性变更通知(如GUI数据绑定)与JavaBean兼容,支持详细事件数据依赖JavaBean规范

谁触发谁更新

  • 由目标对象的状态设定操作在改变目标对象的状态后自动调用 Notify。这种方法的优点是客户不需要记住要在目标对象上调用 Notify,缺点是多个连续的操作会产生多次连续地更新, 可能效率较低。

  • 让客户负责在适当的时候调用 Notify。这样做的优点是客户可以在一系列的状态改变完成后再一次性地触发更新 ,避免了不必要的中间更新。缺点是给客户增加了触发更新的责任。由于客户可能会忘记调用 Notify,这种方式较易出错

封装更复杂的更新语义

当目标和观察者间的依赖关系特别复杂时 ,可能需要一个维护这些关系的对象。我们称这样的对象为更改管理器(Change Manager)。

它的目的是尽量减少观察者反映其目标的状态变化所需的工作量。例如 , 如果一个操作涉及到对几个相互依赖的目标进行改动, 就必须保证仅在所有目标都已更改完毕后,才一次性地通知它们的观察者 ,而不是每个目标都通知观察者

ChangeManager

This post is licensed under CC BY 4.0 by the author.