设计模式中的观察者模式-阿里云开发者社区

开发者社区> c位出道> 正文

设计模式中的观察者模式

简介: 设计模式中的观察者模式
+关注继续查看

观察者模式是一种软件设计模式,其中一个名为主体(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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
大话设计模式之观察者模式
          从前,有个放羊娃,每天都去山上放羊,一天,他觉得十分无聊,就想了个捉弄大家寻开心的主意。他向着山下正在种田的农夫们大声喊:“狼来了!狼来了!救命啊!”农夫们听到喊声急忙拿着锄头和镰刀往山上跑,他们边跑喊:“不要怕,孩子,我们来帮你打恶狼!”农夫们气喘吁吁地赶到山上一看,连狼的影子也没有!放羊娃哈哈大笑:“真有意思,你们上当了!”农夫们生气地走了。
992 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
10099 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
12078 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
13897 0
C#事件和观察者设计模式
  Observer设计模式简介现在假设热水器由三部分组成:热水器、警报器、显示器,它们来自于不同厂商并进行了组装。那么,应该是热水器仅仅负责烧水,它不能发出警报也不能显示水温;在水烧开时由警报器发出警报、显示器显示提示和水温。   Observer 设计模式中主要包括如下两类对象 Subject:监视对象,它往往包含着其他对象所感兴趣的内容。在本范例中,热水器就是一个监视对象,它
1034 0
设计模式之观察者模式
写作方法:    一般用于异步通讯1. 定义ObServer类   定义允许调用的虚函数,保护其他函数使不可见。
588 0
设计模式(二十) 观察者模式
观察者模式也是一种行为型模式,它的作用是将被观察者的任何状态变化传递给观察者。观察者模式在GUI编程的双向绑定等领域都有应用。只要我们希望实现一个类似消息订阅、接收的模式,就可以使用观察者模式。
617 0
观察者设计模式
观察者模式 Observer   观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。   这个主题对象在状态上发生变化时,会通知所有观察者对象,让它们能够自动更新自己。   观察者模式的组成   抽象主题角色 - Subject:把所有对观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的
981 0
+关注
2316
文章
0
问答
来源圈子
更多
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载