0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

观察者模式,超详细!

马哥Linux运维 来源:马哥Linux运维 2023-08-21 16:06 次阅读

设计模式提供了软件开发过程中的一些最佳实践,可以帮助我们解决常见的编程问题,提高软件的可维护性和可复用性,并使我们的代码更加健壮和灵活。设计模式可以带来以下好处:提高代码的可读性和可维护性、提高软件的可复用性、提高开发效率、提高系统的灵活性和可扩展性。今天我们讲一下观察者模式的具体应用。

观察者模式是一种软件设计模式,它允许一个对象(称为“主题”)管理其依赖项(称为“观察者”),它定义了对象之间的一对多依赖关系,当一个对象状态发生改变时,其相关依赖项将会自动收到通知。这种模式提供了一种灵活的方式,将一个对象的状态与依赖它的多个对象联系起来。

fb6d19f2-3f6f-11ee-ac96-dac502259ad0.png

在观察者模式中,主题和观察者之间建立了一种订阅关系。主题负责维护其状态并提供一个注册表,用于存储与其相关联的观察者对象。当主题的状态发生改变时,它会自动通知所有与之相关联的观察者,并传递相应的参数。观察者接收到通知后,可以执行相应的操作来响应主题状态的改变。

意图

观察者模式是一种行为设计模式允许你定义一种订阅机制可在对象事件发生时通知多个观察该对象的其他对象

问题

假如你有两种类型的对象顾客商店顾客对某个特定品牌产品非常感兴趣例如最新型号的 iPhone 手机而该产品很快将会在商店里出售

顾客可以每天来商店看看产品是否到货但如果商品尚未到货时绝大多数来到商店的顾客都会空手而归

另一方面每次新产品到货时商店可以向所有顾客发送邮件可能会被视为垃圾邮件这样部分顾客就无需反复前往商店了但也可能会惹恼对新产品没有兴趣的其他顾客

我们似乎遇到了一个矛盾要么让顾客浪费时间检查产品是否到货要么让商店浪费资源去通知没有需求的顾客

解决方案

拥有一些值得关注的状态的对象通常被称为由于它要将自身的状态改变通知给其他对象我们也将其称为publisher所有希望关注发布者状态变化的其他对象被称为subscribers

观察者模式建议你为发布者类添加订阅机制让每个对象都能订阅或取消订阅发布者事件流不要害怕这并不像听上去那么复杂实际上该机制包括 1一个用于存储订阅者对象引用的列表成员变量2几个用于添加或删除该列表中订阅者的公有方法

fbd70240-3f6f-11ee-ac96-dac502259ad0.png

订阅机制允许对象订阅事件通知

现在无论何时发生了重要的发布者事件它都要遍历订阅者并调用其对象的特定通知方法

实际应用中可能会有十几个不同的订阅者类跟踪着同一个发布者类的事件你不会希望发布者与所有这些类相耦合此外如果他人会使用发布者类那么你甚至可能会对其中的一些类一无所知

因此所有订阅者都必须实现同样的接口发布者仅通过该接口与订阅者交互接口中必须声明通知方法及其参数这样发布者在发出通知时还能传递一些上下文数据

fbf1fa0a-3f6f-11ee-ac96-dac502259ad0.png

发布者调用订阅者对象中的特定通知方法来通知订阅者

如果你的应用中有多个不同类型的发布者且希望订阅者可兼容所有发布者那么你甚至可以进一步让所有发布者遵循同样的接口该接口仅需描述几个订阅方法即可这样订阅者就能在不与具体发布者类耦合的情况下通过接口观察发布者的状态

真实世界类比

如果你订阅了一份杂志或报纸那就不需要再去报摊查询新出版的刊物了出版社即应用中的发布者会在刊物出版后甚至提前直接将最新一期寄送至你的邮箱中

出版社负责维护订阅者列表了解订阅者对哪些刊物感兴趣当订阅者希望出版社停止寄送新一期的杂志时他们可随时从该列表中退出

我们看一段代码示例,然后再通过示例进行分析。在JavaScript中,我们可以使用原型或类来实现观察者模式。下面是一个使用原型的实现示例:

// 观察者接口  
var Observer = function() {};  


Observer.prototype.update = function(data) {};  


// 具体观察者  
var ConcreteObserver1 = function() {};  
ConcreteObserver1.prototype = Object.create(Observer.prototype);  
ConcreteObserver1.prototype.constructor = ConcreteObserver1;  
ConcreteObserver1.prototype.update = function(data) {  
  console.log('ConcreteObserver1 received data: ' + data);  
};  


// 具体观察者  
var ConcreteObserver2 = function() {};  
ConcreteObserver2.prototype = Object.create(Observer.prototype);  
ConcreteObserver2.prototype.constructor = ConcreteObserver2;  
ConcreteObserver2.prototype.update = function(data) {  
  console.log('ConcreteObserver2 received data: ' + data);  
};  


// 主题  
var Subject = function() {  
  this.observers = [];  
};  


Subject.prototype.registerObserver = function(observer) {  
  this.observers.push(observer);  
};  


Subject.prototype.notifyObservers = function(data) {  
  for (var i = 0; i < this.observers.length; i++) {  
    this.observers[i].update(data);  
  }  
};  


// 具体主题  
var ConcreteSubject = function() {};  
ConcreteSubject.prototype = Object.create(Subject.prototype);  
ConcreteSubject.prototype.constructor = ConcreteSubject;  
ConcreteSubject.prototype.setState = function(data) {  
  this.notifyObservers(data);  
};

在上面的代码中,我们首先定义了一个Observer接口,它包含一个update方法。然后我们创建了两个具体的观察者ConcreteObserver1和ConcreteObserver2,它们都实现了Observer接口的update方法。接着我们定义了一个主题Subject,它包含一个观察者数组和一个注册方法registerObserver,以及一个通知方法notifyObservers。最后我们创建了一个具体主题ConcreteSubject,它继承了Subject的原型并实现了一个setState方法,该方法调用通知方法来通知所有观察者状态改变。

在上面的示例中,我们使用了原型继承来实现Observer接口和具体的观察者类。在实际应用中,我们也可以使用类继承或ES6的class语法来实现这些类。另外,在具体主题ConcreteSubject中,我们通过调用notifyObservers方法来通知所有观察者状态改变,这个方法可以传递一个参数作为通知的内容。在具体观察者的update方法中,我们可以根据传递的参数来执行相应的操作。

除了使用JavaScript实现观察者模式外,我们还可以在其他编程语言和框架中找到这种模式的实现。例如,Redis的订阅模型和WebSocket请求都使用了类似的方式来实现主题和观察者之间的订阅关系。这些实现方式都允许客户端订阅特定主题,并在主题状态发生改变时自动接收通知。

观察者模式适合应用场景

当一个对象状态的改变需要改变其他对象或实际对象是事先未知的或动态变化的时可使用观察者模式

当你使用图形用户界面类时通常会遇到一个问题比如你创建了自定义按钮类并允许客户端在按钮中注入自定义代码这样当用户按下按钮时就会触发这些代码

观察者模式允许任何实现了订阅者接口的对象订阅发布者对象的事件通知你可在按钮中添加订阅机制允许客户端通过自定义订阅类注入自定义代码

当应用中的一些对象必须观察其他对象时可使用该模式但仅能在有限时间内或特定情况下使用

订阅列表是动态的因此订阅者可随时加入或离开该列表

实现方式

  1. 仔细检查你的业务逻辑试着将其拆分为两个部分独立于其他代码的核心功能将作为发布者其他代码则将转化为一组订阅类

  2. 声明订阅者接口该接口至少应声明一个update方法

  3. 声明发布者接口并定义一些接口来在列表中添加和删除订阅对象记住发布者必须仅通过订阅者接口与它们进行交互

  4. 确定存放实际订阅列表的位置并实现订阅方法通常所有类型的发布者代码看上去都一样因此将列表放置在直接扩展自发布者接口的抽象类中是显而易见的具体发布者会扩展该类从而继承所有的订阅行为

    但是如果你需要在现有的类层次结构中应用该模式则可以考虑使用组合的方式将订阅逻辑放入一个独立的对象然后让所有实际订阅者使用该对象

  5. 创建具体发布者类每次发布者发生了重要事件时都必须通知所有的订阅者

  6. 在具体订阅者类中实现通知更新的方法绝大部分订阅者需要一些与事件相关的上下文数据这些数据可作为通知方法的参数来传递

    但还有另一种选择订阅者接收到通知后直接从通知中获取所有数据在这种情况下发布者必须通过更新方法将自身传递出去另一种不太灵活的方式是通过构造函数将发布者与订阅者永久性地连接起来

  7. 客户端必须生成所需的全部订阅者并在相应的发布者处完成注册工作

观察者模式优缺点

  • 优点:

    • 降低目标与观察者之间的耦合关系

    • 支持“广播通信

    • 符合开闭原则

  • 确定:

    • 通知可能会花费很长时间

    • 循环依赖的问题

    • 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的

总结一下,观察者模式适用于任何需要实现一对多依赖关系的场景,使得主题的状态改变可以自动通知给所有的观察者。

  1. 订阅/发布系统:观察者模式可以用于实现订阅/发布系统。主题可以代表各种事件或消息,观察者可以订阅感兴趣的主题并接收相关的通知。

  2. 实时通信:观察者模式可以用于实现实时通信。当某个事件发生时,相关的观察者可以立即得到通知并做出相应的响应。

  3. 数据绑定:在图形用户界面开发中,观察者模式可以用于实现数据绑定。当某个数据源发生改变时,相关的视图可以自动更新。

  4. 事件驱动系统:观察者模式可以用于实现事件驱动系统。当某个事件触发时,相关的观察者可以收到通知并执行相应的操作。

  5. 异步消息处理:在分布式系统中,观察者模式可以用于实现异步消息处理。当某个消息到达时,相关的观察者可以收到通知并处理该消息。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 存储
    +关注

    关注

    12

    文章

    3859

    浏览量

    84666
  • 软件设计
    +关注

    关注

    3

    文章

    55

    浏览量

    17675
  • 代码
    +关注

    关注

    30

    文章

    4555

    浏览量

    66772
  • 模式
    +关注

    关注

    0

    文章

    63

    浏览量

    13285

原文标题:观察者模式,超详细!

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    如何观察重复的公告?

    我能够建立和运行“第10天”观察员样本项目。我在另一个DeV板上运行了一个信标,当我启动观察者时,它报告了“不可连接的单向广告”,但是没有新的事件,尽管我知道信标必须定期发布一个广告。作为证据,如果
    发表于 10-14 07:06

    CC2540广播角色和观察者角色切换代码怎么编写?

    希望一个CC2540先通过观察者角色获取其他广播的广播数据,然后在切换为广播角色将这些数据广播给另外一个观察者?这样就需要编程实现观察者
    发表于 03-16 10:27

    RN4020观察者模式无法正常工作怎么回事

    中心,支持MLDP,并使UART流控制R,1//重新引导,使更改生效J,1//观察者模式你对这个问题有什么想法?谢谢,弗朗西斯科
    发表于 04-22 09:03

    属性观察者的特点

    属性观察者,类似于触发器。用来监视属性的除初始化之外的属性值变化,当属性值发生改变时可以对此作出响应。有如下特点: 1,不仅可以在属性值改变后触发didSet,也可以在属性值改变前触发willSet
    发表于 11-04 07:10

    观察者模式在嵌入式编程设计中有何作用

    观察者模式是最常见的模式之一。这种模式提供一种方法来时对象“监听”其他对象,而不需要修改任何数据服务器。在嵌入式领域,这意味着数据能够很容易分享给其他元素。
    发表于 12-22 08:31

    低功耗蓝牙的GAP具有哪几种模式

    1、低功耗蓝牙的GAP(通用属性规范)有四个角色: 广播(广播),观察者(扫描),外围设备(通常说的从设备),中央设备(通常说的主设备)2、
    发表于 12-23 06:05

    低功耗蓝牙三种发现模式是什么

    1、低功耗蓝牙的GAP(通用属性规范)有四个角色: 广播(广播),观察者(扫描),外围设备(通常说的从设备),中央设备(通常说的主设备)2、三种发现
    发表于 12-23 07:55

    基于观察者模式的屏幕布局控件设计

    观察者模式作为设计模式中行为模式的一种,解决了上述具有一对多依赖关系对象重用问题。文中在分析观察者模式
    发表于 02-13 16:20 4次下载
    基于<b class='flag-5'>观察者</b><b class='flag-5'>模式</b>的屏幕布局控件设计

    Java设计模式分析之观察者

    观察者模式的流程跟报纸订阅方式一致,即:观察者模式=出版者+订阅者,只是名称不一样,出版者改称为主题(Subject),订阅者改称为观察者
    发表于 09-26 17:36 0次下载

    在 Java8 环境下实现观察者模式的实例分析

    观察者(Observer)模式又名发布-订阅(Publish/Subscribe)模式,是四人组(GoF,即 Erich Gamma、Richard Helm、Ralph Johnson
    发表于 10-12 16:09 0次下载
    在 Java8 环境下实现<b class='flag-5'>观察者</b><b class='flag-5'>模式</b>的实例分析

    大陆正在研发一款路况观察者应用

    据外媒报道,大陆正在研发一款路况观察者(Road Condition Observer)应用。随着传感器及摄像头技术的愈发成熟,再结合车载流媒体数据,或许能够完成该应用的设计。此外,工程师还将为该应用写入成熟的算法。
    发表于 05-15 11:30 887次阅读

    设计模式行为型:观察者模式

    定义对象之间的一种一对多依赖关系,使得每一个对象发生状态的变化时,其相关依赖对象皆得到通知并被自动更新,又称为发布-订阅模式、模型-视图模式、源-监听器模式或从属者模式
    的头像 发表于 06-07 16:56 465次阅读
    设计<b class='flag-5'>模式</b>行为型:<b class='flag-5'>观察者</b><b class='flag-5'>模式</b>

    基于观察者模式设计的框架-REB,使代码模块化

    设计模式里面的观察者模式,一直是作者想去设计一套框架来阐述这一个模式,因此REB(Rice Event Broker)就是为了完成观察者
    的头像 发表于 10-17 09:35 314次阅读
    基于<b class='flag-5'>观察者</b><b class='flag-5'>模式</b>设计的框架-REB,使代码模块化

    一文解析BLE观察者模式回调机制

    nRF5 SDK从版本14开始,对事件回调机制做了更新,引入了观察者模式,以解耦不同BLE Layer对BLE事件的回调函数。
    的头像 发表于 11-27 10:07 365次阅读
    一文解析BLE<b class='flag-5'>观察者</b><b class='flag-5'>模式</b>回调机制

    什么是观察者设计模式?Golang中的观察者模式介绍

    当涉及到订单处理系统时,观察者设计模式可以用于实现订单状态的变化和通知。
    的头像 发表于 01-08 10:08 190次阅读