当前位置: 首页 > 后端技术 > Node.js

设计模式中的观察者模式

时间:2023-04-04 01:35:11 Node.js

观察者模式是一种软件设计模式,其中一个名为Subject的对象维护着一个其依赖关系的列表,称为观察者,通常调用它们(observers)自动通知它们任何状态的方式之一变化。观察者模式主要用于在“事件驱动”软件中实现分布式事件处理系统。在这些系统中,Subject通常被称为“事件流”或“事件流的源”,而观察者被称为“事件接收器”。流命名法暗示了一种物理设置,其中观察者在物理上是分开的,并且无法控制从主题/流源发出的事件。这种模式非常适合任何进程,在这些进程中,数据来自某些输入,这些输入在启动时对CPU不可用,而是“随机”(HTTP请求、GPIO数据、来自键盘/鼠标/...的用户输入、分布式数据库)和区块链,...)。大多数现代编程语言都包含实现观察者模式组件的内置“事件”结构。虽然不是强制性的,但大多数“观察者”实现将使用后台线程来监听主题事件和内核提供的其他支持机制(Linuxepoll,...)。观察者设计模式是二十三个著名的“四人组”设计模式之一,它描述了如何解决设计灵活且可重用的面向对象软件时反复出现的设计挑战,即更容易实现、更改、测试和重用。观察者设计模式可以解决什么问题?观察者模式解决了对象之间应该定义一对多依赖关系的问题,而不是让对象紧耦合。它应该确保当一个对象改变状态时,无限数量的依赖对象被自动更新。一个对象应该可以通知无限数量的其他对象。通过定义直接更新依赖对象状态的对象(主体)来定义对象之间的一对多依赖关系是不灵活的,因为它将主体耦合到特定的依赖对象。尽管如此,从性能的角度来看,或者如果对象实现是紧密耦合的(想想每秒执行数千次的低级内核结构),它仍然有意义。在某些情况下,紧密耦合的对象可能难以实现和重用,因为它们引用和了解(以及如何更新)许多具有不同接口的不同对象。在其他情况下,紧耦合对象可能是更好的选择,因为编译器将能够在编译时检测错误并在CPU指令级别优化代码。观察者设计模式描述了什么解决方案?定义主题和观察者对象。这样当一个主题改变状态时,所有注册的观察者都会被自动通知和更新(可能是异步的)。委托人的唯一责任是维护观察者列表并通过调用它们的update()操作通知它们状态更改。观察者的作用是在一个主题上注册(和取消注册)自己(以通知状态更改)并在收到通知时更新他们的状态(以将他们的状态与主题的状态同步)。这使得主体和观察者松耦合。主体和观察者对彼此没有清晰的感知。可以在运行时独立地添加和删除观察者。这种通知-注册交互也称为发布-订阅。强引用与弱引用观察者模式可能导致内存泄漏,称为无效侦听器问题,因为在基础实现中,它需要显式注册和显式注销,就像在处理模式中一样,因为对象持有对观察者的强引用,使它们保持活动状态。这可以通过主体对观察者的弱引用来防止。耦合和典型的pub-sub实现通常,观察者模式的实现使得被“观察”的“主体”是观察状态变化(并与观察者通信)的对象的一部分。这种类型的实现被认为是“紧耦合”,迫使观察者和代理相互了解并可以访问它们的内部部分,从而导致可扩展性、速度、消息恢复和维护(也称为事件或通知)可能出现的问题损失),条件分散缺乏灵活性,并且可能阻碍所需的安全措施。在发布-订阅模式(又名发布-订阅模式)的一些(非轮询)实现中,这是通过创建一个专用的“消息队列”服务器(有时是一个额外的“消息处理程序”对象)作为附加阶段来解决的在观察者和观察对象之间,从而解耦组件。在这些情况下,消息队列服务器由使用观察者模式的观察者访问,“订阅一些消息”只知道预期的消息(或在某些情况下不知道),而对消息的发送者本身一无所知;发送者也可能对观察者一无所知。发布-订阅模式的其他实现,实现类似的通知和与感兴趣的各方通信的效果,根本不使用观察者模式。在OS/2和Windows等多窗口操作系统的早期实现中,术语“发布-订阅”和“事件驱动软件开发”被用作观察者模式的同义词。正如GoF书中所述,观察者模式是一个非常基本的概念,并没有解决在观察者被通知特殊逻辑兴趣之前或之后消除对被观察的“主题”或被观察的“主题”所做的更改的需要。此模式也不处理发送更改通知或保证更改通知时的日志记录。这些问题通常在消息队列系统中处理,观察者模式只是其中的一小部分。观察者模式的UML和时序图在上面的UML类图中,Subject类并不直接更新依赖对象的状态。相反,Subject引用Observer接口(update())来更新状态,这使得Subject独立于依赖对象的状态如何更新。Observer1和Observer2类通过将它们的状态与主体的状态同步来实现Observer接口。UML序列图显示了运行时交互:Observer1和Observer2对象调用Subject1上的attach(this)来注册自己。假设Subject1的状态发生变化,Subject1调用自己的notify()。notify()在已注册的Observer1和Observer2对象上调用update(),这两个对象从Subject1请求更改数据(getState())以更新(同步)它们的状态。UML类图看一个Java例子。主题即数据源的实现:importjava.util.List;importjava.util.ArrayList;importjava.util.Scanner;classEventSource{publicinterfaceObserver{voidupdate(Stringevent);}privatefinalListobservers=newArrayList<>();privatevoidnotifyObservers(Stringevent){observers.forEach(observer->observer.update(event));}publicvoidaddObserver(观察者观察者){observers.add(观察者);}publicvoidscanSystemIn(){扫描仪scanner=newScanner(System.in);while(scanner.hasNextLine()){Stringline=scanner.nextLine();notifyObservers(行);}}}Observer的实现:publicclassObserverDemo{publicstaticvoidmain(String[]args){System.out.println("EnterText:");EventSourceeventSource=newEventSource();eventSource.addObserver(event->{System.out.println("收到响应:"+event);});eventSource.scanSystemIn();}}JavaScript的现实:letSubject={_state:0,_observers:[],add:function(observer){this._observers.push(observer);},getState:function(){returnthis._state;},setState:function(value){this._state=value;for(leti=0;i