设计模式中的观察者模式

简介: 设计模式中的观察者模式

观察者模式是一种软件设计模式,其中一个名为主体(Subject)的对象维护其依赖项列表,称为观察者,并通常通过调用它们(observers)的方法之一来自动通知它们任何状态更改。


观察者模式主要用于在“事件驱动”软件中实现分布式事件处理系统。在这些系统中,主体 Subject 通常被称为“事件流(stream of events)”或“事件流源”,而观察者被称为“事件接收器”。


流命名法暗示了一种物理设置,其中观察者在物理上是分开的,并且无法控制从主题/流源发出的事件。


这种模式非常适合任何进程,其中数据从启动时 CPU 不可用的某些输入到达,而是“随机”到达(HTTP 请求、GPIO 数据、来自键盘/鼠标/的用户输入…、分布式数据库)和区块链,…)。


大多数现代编程语言都包含实现观察者模式组件的内置“事件”结构。虽然不是强制性的,但大多数“观察者”实现将使用后台线程监听主题事件和内核提供的其他支持机制(Linux epoll,…)。


观察者设计模式是二十三个著名的“四人帮”设计模式之一,描述了如何解决反复出现的设计挑战,以设计灵活且可重用的面向对象软件,即更容易实现、更改、 测试和重用。


What problems can the Observer design pattern solve?

观察者模式解决了以下问题:


对象之间的一对多依赖关系应该在不使对象紧密耦合的情况下定义。

应该确保当一个对象改变状态时,自动更新无限数量的依赖对象。

一个对象可以通知无限数量的其他对象应该是可能的。

通过定义一个直接更新依赖对象状态的对象(主体)来定义对象之间的一对多依赖是不灵活的,因为它将主体耦合到特定的依赖对象。尽管如此,从性能的角度来看,或者如果对象实现是紧密耦合的(想想每秒执行数千次的低级内核结构),它仍然有意义。在某些情况下,紧密耦合的对象可能难以实现,并且难以重用,因为它们引用并了解(以及如何更新)具有不同接口的许多不同对象。在其他情况下,紧密耦合的对象可能是更好的选择,因为编译器将能够在编译时检测错误并在 CPU 指令级别优化代码。


What solution does the Observer design pattern describe?

定义主题和观察者对象。


这样当一个主题改变状态时,所有注册的观察者都会被自动通知和更新(可能是异步的)。


主体的唯一职责是维护观察者列表并通过调用它们的 update() 操作通知它们状态变化。 观察者的职责是在一个主题上注册(和取消注册)自己(以获得状态变化的通知)并在收到通知时更新他们的状态(将他们的状态与主题的状态同步)。 这使得主体和观察者松散耦合。 主体和观察者彼此之间没有明确的感知。 可以在运行时独立添加和删除观察者。 这种通知-注册交互也称为发布-订阅。


Strong vs. weak reference

观察者模式会导致内存泄漏,称为失效侦听器问题,因为在基本实现中,它需要显式注册和显式取消注册,就像在处置模式中一样,因为主体持有对观察者的强引用,使它们保持活动状态。 这可以通过主体持有对观察者的弱引用来防止。


Coupling and typical pub-sub implementations

通常,观察者模式被实现,因此被“观察”的“主体”是正在观察状态变化的对象的一部分(并传达给观察者)。这种类型的实现被认为是“紧密耦合的”,迫使观察者和主体相互了解并可以访问它们的内部部分,从而产生可扩展性、速度、消息恢复和维护(也称为事件或通知)的可能问题损失),条件分散缺乏灵活性,以及可能妨碍所需的安全措施。在发布-订阅模式(又名发布-订阅模式)的一些(非轮询)实现中,这是通过创建一个专用的“消息队列”服务器(有时还有一个额外的“消息处理程序”对象)作为额外阶段来解决的观察者和被观察对象之间,从而解耦组件。在这些情况下,消息队列服务器由观察者使用观察者模式访问,“订阅某些消息”只知道预期的消息(或在某些情况下不知道),而对消息发送者本身一无所知;发送者也可能对观察者一无所知。发布订阅模式的其他实现,实现了类似的通知和向感兴趣的各方通信的效果,根本不使用观察者模式。


在 OS/2 和 Windows 等多窗口操作系统的早期实现中,术语“发布-订阅模式”和“事件驱动的软件开发”被用作观察者模式的同义词。


正如 GoF 书中所描述的,观察者模式是一个非常基本的概念,并没有解决在通知观察者之前或之后消除对观察到的“主体”或被观察“主体”所做的特殊逻辑的更改的兴趣。该模式也不处理发送更改通知时的记录或保证收到更改通知。这些问题通常在消息队列系统中处理,其中观察者模式只是其中的一小部分。


观察者模式的 UML 和 时序图


image.png在上面的UML类图中,Subject类并没有直接更新依赖对象的状态。 相反,Subject 引用 Observer 接口(update())来更新状态,这使得 Subject 独立于依赖对象的状态如何更新。 Observer1 和 Observer2 类通过将它们的状态与主题的状态同步来实现 Observer 接口。

UML 序列图显示了运行时交互:Observer1 和Observer2 对象调用Subject1 上的attach(this) 来注册它们自己。 假设 Subject1 的状态发生了变化,Subject1 会对其自身调用 notify() 。

notify() 对已注册的 Observer1 和 Observer2 对象调用 update(),它们从 Subject1 请求更改的数据 (getState()) 以更新(同步)它们的状态。


image.pngSubject 即数据源的实现:

import java.util.List;
import java.util.ArrayList;
import java.util.Scanner;
class EventSource {
    public interface Observer {
        void update(String event);
    }
    private final List<Observer> observers = new ArrayList<>();
    private void notifyObservers(String event) {
        observers.forEach(observer -> observer.update(event));
    }
    public void addObserver(Observer observer) {
        observers.add(observer);
    }
    public void scanSystemIn() {
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine();
            notifyObservers(line);
        }
    }
}

Observer 的实现:

public class ObserverDemo {
    public static void main(String[] args) {
        System.out.println("Enter Text: ");
        EventSource eventSource = new EventSource();
        eventSource.addObserver(event -> {
            System.out.println("Received response: " + event);
        });
        eventSource.scanSystemIn();
    }
}

JavaScript 的实现:

let Subject = {
    _state: 0,
    _observers: [],
    add: function(observer) {
        this._observers.push(observer);
    },
    getState: function() {
        return this._state;
    },
    setState: function(value) {
        this._state = value;
        for (let i = 0; i < this._observers.length; i++)
        {
            this._observers[i].signal(this);
        }
    }
};
let Observer = {
    signal: function(subject) {
        let currentValue = subject.getState();
        console.log(currentValue);
    }
}
Subject.add(Observer);
Subject.setState(10);
//Output in console.log - 10
相关文章
|
3月前
|
设计模式 监控 安全
设计模式 | 观察者模式
设计模式 | 观察者模式
18 0
|
3月前
|
设计模式 前端开发 数据中心
设计模式之观察者模式
设计模式之观察者模式
|
6月前
|
设计模式 安全 C++
设计模式之观察者模式(C++)
设计模式之观察者模式(C++)
|
1月前
|
设计模式 存储 Java
【设计模式】观察者模式
【设计模式】观察者模式
|
3月前
|
设计模式 JavaScript 前端开发
【设计模式】之观察者模式
观察者模式是一种常用的设计模式,在前端开发中有广泛应用。它通过定义一种一对多的依赖关系,实现了对象之间的解耦和灵活性。通过使用观察者模式,可以实现事件处理、数据绑定、响应式系统等功能。然而,需要根据具体情况权衡使用观察者模式所带来的优缺点。
25 0
|
7月前
|
设计模式 前端开发
设计模式~~~观察者模式
设计模式~~~观察者模式
28 0
|
7月前
|
设计模式 C++
C++实现设计模式之观察者模式
什么是观察者模式? 观察者模式是一种一对多的以来关系,当一个对象的状态发生改变时,所有依赖于他的对象都得到通知并被自动更新。它的主体是通知的发布者,发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知,将观察者和被观察的对象分离开。
|
设计模式
设计模式-15-观察者模式
观察者模式(Observer Pattern)定义了一种对象间一对多的依赖关系,一个主题目标对象能被多个观察者对象同时监听,使得每当主题目标对象状态变化时,所有依赖它的对象都会得到通知并被自动更新,属于行为型设计模式。
69 0
|
设计模式 资源调度 JavaScript
设计模式之观察者模式(5)
设计模式之观察者模式(5)
78 0
|
设计模式 消息中间件 存储
浅谈设计模式 - 观察者模式(四)
浅谈设计模式 - 观察者模式(四)
136 0