首页 > 其他分享 >设计模式-----观察者模式

设计模式-----观察者模式

时间:2024-03-28 15:58:44浏览次数:34  
标签:订阅 eating Observer eventType 观察者 console ----- params 设计模式

一、概念

1、Observer模式的概念       

      (Observer)被称作发布-订阅者模式或消息机制,定义了一种依赖关系,解决了主体对象与观察者之间功能的耦合。      

      (Observer)模式是行为模式之一,它的作用是当一个对象的状态发生变化时,能够自动通知其他关联对象,自动刷新对象状态。     

好处:

  • 可广泛应用于异步编程中,是一种替代传递回调函数的方案。       
  • 可取代对象之间硬编码的通知机制,一个对象不用再显示地调用另外一个对象的某个接口。 两对象轻松解耦。          

2、Observer模式的角色       

Subject(被观察者)        

被观察的对象。当需要被观察的状态发生变化时,需要通知队列中所有观察者对象。Subject需要维持(添加,删除,通知)一个观察者对象的队列列表。         

ConcreteSubject -- 被观察者的具体实现。包含一些基本的属性状态及其他操作。       

Observer(观察者)         

接口或抽象类。当Subject的状态发生变化时,Observer对像将通过一个callback函数得到通知。   

ConcreteObserver -- 观察者的具体实现。得到通知后将完成一些具体的业务逻辑处理。     

二、 应用场景

<script>  
let subscribe = { 'eating': [] };
    // 订阅
    subscribe.eating[0] = () => {
      console.log('我是Ben,我要去吃饭');
    }
    subscribe.eating[1] = () => {
      console.log('我是James,我要去吃饭');
    }
    subscribe.eating[2] = () => {
      console.log('我是Oscar,我要去吃饭');
    }
    // 发布
    let treat = (() => {
      let eatingPersion = subscribe.eating;
      let len = eatingPersion.length;
      for (let i = 0; i < len; i++) {
        subscribe.eating[i]();
      }
    })();
    /*
      如果Judy也想去吃饭的话,不需要告诉任何人, 只需要自己订阅就好啦。
      But这里有个缺陷,就是Ben今天想减肥,想要取消订阅,没有提供取消订阅的的机制
    */
    // 完善先取消订阅模式
    var Observer = (function () {
      //静态私有变量,用来存放不对外暴露的消息列队
      var _rank = {};
      return {
        //订阅消息接口
        register: function (type, fn) {
          //如果这个消息的方法不存在,则放入消息列队中
          if (typeof _rank[type] === 'undefined') {
            _rank[type] = [fn];
          }
          //如果这个消息的方法是存在的,就给这个消息的数组中添加这个方法
          else {
            _rank[type].push(fn);
          }
        },
        //发布消息接口
        fire: function (type, argsJson) {
          //如果消息未定义,就返回
          if (!_rank[type]) { return; }
          //对消息的参数进行包装
          var newArgs = {
            type: type,
            args: argsJson || {}
          };
          //遍历执行这个消息列队中的方法
          for (var i = 0, l = _rank[type].length; i < l; i++) {
            _rank[type][i].call(this, newArgs);
          }
        },
        //消息注销方法
        remove: function (type, fn) {
          //确保这个消息列队是存在的
          if (_rank[type] instanceof Array) {
            //从最后一个方法开始查找
            for (var i = _rank[type].length - 1; i >= 0; i--) {
              //如果存在就从列队中删除
              _rank[type][i].toString() === fn.toString() && _rank[type].splice(i, 1);
            }
          }
        }
      }
    })();
    // PM瑞瑞发布
    Observer.fire('eating', { msg: 'pm瑞瑞请客~' });
    // 订阅
    Observer.register('eating', function (params) {
      console.log(params.type + ': 我是Ben,我要去吃饭,' + params.args.msg);
    });
    Observer.register('eating', function (params) {
      console.log(params.type + ': 我是James,我要去吃饭,' + params.args.msg);
    });
    Observer.register('eating', function (params) {
      console.log(params.type + ': 我是Oscar,我要去吃饭,' + params.args.msg);
    });
    // Ben取消订阅
    Observer.remove('eating', function (params) {
      console.log(params.type + ': 我是Ben,我要去吃饭,' + params.args.msg);
    });
    // PM瑞瑞重新发布
    Observer.fire('eating',{msg:'pm瑞瑞请客~'});

    /*
      此时,如果还有人要吃饭,只需要订阅一个位置就好,并且其它的活动也可以通过模式去订阅,
      发布,撤销~。观察者的使用环境是这样的,例如:多人协作中在初始化函数init()中不同人
      都要加事件,如果直接添加,总是怕出问题,此时,如果通过定义一个观察者,
      就方便多了,可以放心大胆的操作。又例如年久失修的代码要更改,但是关系错中复杂,最好的
      方式就是给事件都归入消息列队,然后添加自己的事件,这样就万事大吉了~
    */
 </script>

 

/**
  * 发布订阅模式(观察者模式)
  * handles: 
  * on: 订阅事件
  * emit: 发布事件
  * off: 删除事件
 **/
class PubSub {
  constructor() {
    this.handles = {} // 事件处理函数集合
  }
  //订阅事件
  on(eventType, handle) {
    if (!this.handles.hasOwnProperty(eventType)) {
      this.handles[eventType] = []
    }
    if (typeof handle == 'function') {
      this.handles[eventType].push()
    } else {
      throw new Error('缺少回调函数')
    }
    return this;
  }
  // 发布事件
  emit(eventType, ...args) {
    if (this.handles.hasOwnProperty(eventType)) {
      this. Handles[eventType].forEach((item, key, arr) => {
        item.apply(null, args)
      })
    } else {
      throw new Error(`"${eventType}"事件未注册`)
    }
    return this;
  }
  // 删除事件
  off(eventType, handle) {
    if (!this.handles.hasOwnProperty(eventType)) {
      throw new Error(`"${eventType}"事件未注册`)
    } else if (typeof handle != 'function') {
      throw new Error('缺少回调函数')
    } else {
      this.handles[eventType].forEach((item, key, arr) => {
        if (item == handle) {
          this.handles[eventType].splice(key, 1)
        }
      })
    }
    return this;
  }
}
let pubsub = new PubSub();
function callback() {
  console.log('you are so nice')
}
// 订阅:如下在事件eventTypeName上添加了两个回调
pubsub.on('eventTypeName', (...args) => {
  console.log(args.join(' '))
}).on('eventTypeName', callback)
// 发布:
pubsub.emit('eventTypeName', 'whar', 'a', 'fucking day'); //发布 依次执行 eventTypeName 事件中的 所以方法
pubsub.off('eventTypeName', callback)  // 删除只是删除 eventTypeName 事件的 callback 方法
pubsub.emit('eventTypeName', 'fucking', 'again')
/*
  输出值:
  what a fucking day
  you are so nice
  fucking again
*/

 总结:   发布—订阅模式的优点非常明显,一为时间上的解耦,二为对象之间的解耦。它的应用非常广泛,既可以   用在异步编程中,也可以帮助我们完成更松耦合的代码编写。发布—订阅模式还可以用来帮助实现一些别   的设计模式,比如中介者模式。从架构上来看,无论是 MVC 还是 MVVM, 都少不了发布—订阅模式的参   与,而且JavaScript本身也是一门基于事件驱动的语言。   当然,发布—订阅模式也不是完全没有缺点(浪费内存)。创建订阅者本身要消耗一定的时间和内存,   而且当你订阅一个消息后,也许此消息最后都未发生,但这个订阅者会始终存在于内存中。另外,发   布—订阅模式虽然可以弱化对象之间的联系,但如果过度使用的话,对象和对象之间的必要联系也将被   深埋在背后,会导致程序难以跟踪维护和理解。特别是有多个发布者和订阅者(b订阅a的消息并发布   给c)嵌套到一起的时候,要跟踪一个bug不是件轻松的事情。

标签:订阅,eating,Observer,eventType,观察者,console,-----,params,设计模式
From: https://blog.csdn.net/qq_15557073/article/details/137089343

相关文章

  • SD-WAN如何适应运营商网络
    随着企业网络需求的不断增长,SD-WAN(软件定义广域网)技术成为提升网络灵活性、可管理性和性能的重要工具。然而,要实现SD-WAN的有效部署,必须与运营商网络进行密切集成和适应。以下是SD-WAN如何适应运营商网络的关键方面:多路径与负载均衡:SD-WAN能够利用多种连接路径,包括专线、互联......
  • 重磅-华为全套企业管理资料合集21专题(附下载)
    知识星球下载(文末领取优惠券):https://t.zsxq.com/18s2bUD261.绩效考核华为内训绝密资料:绩效管理与绩效考核.ppt华为绩效管理与绩效考核制度.docx华为公司实用性各种绩效图表汇总.doc华为公司考勤管理制度.doc华为IPD模式中跨部门团队成员的考核激励制度.doc2.企业管理......
  • DSP-CCS12在线仿真,设置断点提示AET资源不够!
    刚刚开始学习DSP,在用CCS12开发环境在线仿真时出现这样的提示:"ThistaskcannotbeaccomplishedwiththeexistingAETresources."网上查找一番,得出这样的解释:CCS会默认载入工程上一次设置的断点,会占用断点资源;另外,手动暂停和使能CIO功能也会占用一个断点。这样很容易就就出现......
  • 前端学习-UI框架学习-Bootstrap5-005-颜色
    菜鸟教程学习链接字体颜色Bootstrap5提供了一些有代表意义的颜色类:.text-muted,.text-primary,.text-success,.text-info,.text-warning,.text-danger,.text-secondary,.text-white,.text-dark,.text-body(默认颜色,为黑色)and.text-light:可以设置文本颜色透明度......
  • P2421-荒岛野人Savage题解
    好久没写题解了啊洛谷P2421荒岛野人题目大意:有一个有很多洞的岛上,住了\(n\)个野人,每个野人的初始位置为\(c[i]\),换洞的速度为\(p[i]\),寿命为\(l[i]\)。要求求出洞的最少个数\(M\)满足每个野人在生存状态下不会在同一年和其他野人住在同一个山洞里。概括版:很多个青蛙的约会。......
  • 设计模式DP-模版模式
    #include<stdio.h>#include<string.h>#include<stdlib.h>typedefstructInterface_t{ /*初始化外设USB、SPI、IIC等*/ void(*init_peripheral)(void*obj); /*初始化硬盘*/ void(*init_disk)(void*obj); /*初始化内存*/ void(*init_memory)(void*obj);......
  • KingbaseES V8R6集群运维案例之---主备failover切换原因分析
    案例说明:生产环境,KingbaseESV8R6的集群发生failover切换,分析集群切换的原因。适用版本:KingbaseESV8R6集群架构:137.xx.xx.67主原备库137.xx.xx.94原主库137.xx.xx.68vip地址一、日志分析1、分析原备库hamgr.log如下所示,通过原备库hamgr.log日志获取到具体......
  • KingbaseES V8R6数据库运维案例之---用户权限导致的备份恢复故障
    案例说明:由于限制了用户对数据库的访问,导致在执行‘sys_backup.shinit’初始化物理备份时,执行失败。适用版本:KingbaseESV8R6一、问题现象如下所示,执行‘sys_backup.shinit’初始化物理备份:1、执行初始化失败[kingbase@node201bin]$shsys_backup.shinitERROR:Con......
  • HCIP-Datacom(H12-821)题库补充(3/27)
                  最新HCIP-Datacom(H12-821)完整题库请扫描上方二维码访问,持续更新中。运行OSPF协议的路由器,所有接口必须属于同一个区域。A:正确B:错误答案:B解析:OSPF的邻居关系是基于接口的,可以不同的接口属于不同的区域。ACL本质上是一种报文......
  • 中国500米逐年植被净初级生产力(NPP)数据集(2000-2022)
      净初级生产力(NPP)是指植物在单位时间单位面积上由光合作用产生的有机物质总量中扣除自养呼吸后的剩余部分,是生产者能用于生长、发育和繁殖的能量值,反映了植物固定和转化光合产物的效率,也是生态系统中其他生物成员生存和繁衍的物质基础。其中涉及的主要参量包括光和有效......