设计模式 之 观察者模式

设计模式是一种解决问题的思路,而非固定的公式

定义:

是一对多的关系依赖关系,当被依赖的对象的状态发生变化了,那么所有依赖他的对象都会得到通知

 观察者模式有主体和观察者组成.主体负责发布事件,观察者负责订阅这些事件来观察主体.主体并不知道观察者的任何事情,观察者知道主体能注册事件的回调函数

优点: 
      1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。

缺点: 
      1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 
      2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 
      3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

白话解释:

新出来的苹果手机,旺季,供货不足,当我们去店铺买时,告知没有货了,可以留下你的联系方式,等来货时,就会第一个通知你,不用自己跑去店铺经常询问
 
分析:
1.订阅方法:我们把手机留下,属于一种消息订阅的消息,需要一个订阅行为的具体方法
2.预定列表:因为是新版的,不止我们一个人去预定,所以需要预定列表统计所有的预订者的联系方式及版本信息,key:联系方式,value版本型号
3.发布方法:到货了,专卖店根据预定列表进行统一发布消息,所以专卖店需要一个发布方法来实行具体的发布行为
4.取消订阅:能订阅也就能取消,在等待的过程中,等不及了想买华为了,那么就不那么想要苹果手机了,为了不想被打扰,那么就取消订阅,需要一个取消订阅的方法来实现具体的取消订阅的行为 

在写案例前,我们在学过的知识里也有用到观察者思想,应用场景有哪些

DOM事件  
    给body订阅了一个click事件,相当于订阅方法
    但是浏览器不知道什么时候点击,等你触发click事件的时候,才会发布通知(发布方法)
 
vue双向绑定v-model
    双向绑定的实现就是一种观察者模式,绑定数据(订阅),浏览器不知道你什么时候修改,你页面上的
    所有绑定了该数据或者依赖该数据的节点,其实就是一个预定列表,只有修改了该数据,vue才会通知(发布方法)
 
简单案例
<script>
     //定义商家
     var sj = {};
     //定义预定列表
     sj.orderList = {}  
     //将增加的订阅者添加到列表中(订阅方法)
     sj.listen = function(id,info){
       //如果存在
       if(!this.orderList[id])
       {
         //预定列表
         this.orderList[id] = [];
       }
       //将用于的
       this.orderList[id].push(info);
     }

     //发布消息(发布方法)
     sj.publish = function(){
       var id = Array.prototype.shift.call(arguments);
       var infos = this.orderList[id];
       if(!infos || infos.length ===0)
       {
         console.log('您还没有预定信息')
         return false;
       }
       for(var i =0 ; i<infos.length;i++)
       {
         console.log('预定成功');
         console.log('尊敬的用户');
         infos[i].apply(this,arguments)
         console.log("到货了")
       }
     }

     //取消订阅
     sj.remove = function(id,fn)
     {
       var infos = this.orderList[id];
       if(!infos){return false}
       if(!fn)
       {
         console.log(123);
       }else{
         for(var i= 0;i<infos.length;i++)
         {
           if(infos[i]===fn)
           {
             infos.splice(i,1);
           }
         }
       }
     }

     let customA = function(){
       console.log('黑色尊享版本一台')
     }

     let arr = [1,2,3,4,5,6]
     for(let i=0;i<arr.length;i++)
     {
       arr.splice(i,1)
     }

     sj.listen('111',customA)
     sj.remove('111',customA)
     sj.publish('111')

   </script>
 参考资料:https://www.cnblogs.com/dengyao-blogs/p/11690605.html
 
 
 第二简单案例
var Event = {
       on:function (eventName,callback)
       {
         if(!this.handle){
          Object.defineProperty(this,'handle',{
            configurable:true, // 是否可以删除
            enumerable:false,//目标属性是否可以被枚举(遍历)
            value:{},
            writable:true  //是否可以重写
          })
         }
         if(!this.handle[eventName])
         {
           this.handle[eventName]=[]
         }
         this.handle[eventName].push(callback)
       },
       emit:function (eventName)
       {
         if(!this.handle[eventName])
         {
           return false
         }
         else{
           for(let i = 0;i<this.handle[eventName].length;i++)
           {
             this.handle[eventName][i](arguments[1])
           }
         }
       }
     }
     /* 测试一 */
     Event.on('test', function (result) {
        console.log(result);
      });
      Event.on('test', function () {
          console.log('test');
      });
      Event.emit('test', 'hello world'); // 输出 'hello world' 和 'test'


      /* 测试二 */
      var person1 = {};
      var person2 = {};
      Object.assign(person1, Event);
      Object.assign(person2, Event);
      person1.on('call1', function () {
          console.log('person1');
      });
      person2.on('call2', function () {
          console.log('person2');
      });
      person1.emit('call1'); // 输出 'person1'
      person1.emit('call2'); // 没有输出
      person2.emit('call1'); // 没有输出c
 
参考地址:https://blog.csdn.net/one_girl/article/details/80325210
 
 
 
 
原文地址:https://www.cnblogs.com/zmztya/p/14635007.html