介绍

观察者模式又叫发布订阅模式(Publish/Subscribe),一个目标对象管理所有相依于它的观察者对象。该模式中存在两个角色:观察者和被观察者。目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。

该模式通常有两种实现策略:"推"(push) 和 "拉"(pull)

例:对于报社可能雇佣投送人员四处送报给订阅者,这就是主动推送。而对于规模小的报社没有足够的资源去雇佣投送员,这时候可以采取 "拉" 的方式。就是在订阅者附近提供自己的数据,供订阅者 "拉" 自己所需的数据。

实现

push

var Observable = {
    observers: []
  , lastId: -1
  , addObserver: function(observer) {
      this.observers.push({
        callback: observer
      , id: ++this.lastId
      })

      return this.lastId
    }
  , removeObserver: function(id) {
      for (var i = this.observers.length - 1; i >= 0; i--) {
        this.observers[i]
        if (this.observers[i].id == id) {
          this.observers.splice(i, 1)
          return true
        }
      }

      return false
    }
  , notifyObservers: function(message) {
      for (var i = this.observers.length - 1; i >= 0; i--) {
        this.observers[i].callback(message)
      };
    }
}

var id_1 = Observable.addObserver(function(message){
  console.log("First observer message:" + message)
})

var observer = function(message){
  console.log("Second observer message:" + message)
}

var id_2 = Observable.addObserver(observer)

Observable.notifyObservers('test 1')
Observable.removeObserver(id_2)
Observable.notifyObservers('test 2')

pull

var Observable = {}

;(function(O){
  var observers = []
    , privateVar

  O.addObserver = function(observer) {
    observers.push(observer)
  }

  O.removeObserver = function(observer) {
    var index = observers.indexOf(observer)

    if (~index) {
      observers.splice(index, 1)
    }
  }

  O.notifyObservers = function() {
    for (var i = observers.length - 1; i >= 0; i--) {
      observers[i].update()
    };
  }

  O.updatePrivate = function(newValue) {
    privateVar = newValue
    this.notifyObservers()
  }

  O.getPrivate = function() {
    return privateVar
  }
}(Observable))

Observable.addObserver({
  update: function(){
    this.process()
  }
, process: function(){
    var value = Observable.getPrivate()
    console.log("Private value is: " + value)
  }
})

Observable.updatePrivate('test 1')
// Private value is: test 1

Observable.updatePrivate('test 2')
// Private value is: test 2

被观察对象将通知所有的观察者,并且每个观察者将从被观察对象中提取所需的信息。

让多个对象都具有观察者发布订阅的功能

var observer = {
    addSubscriber:function (callback) {
        this.subscribers[this.subscribers.length] = callback;
    },
    removeSubscriber:function (callback) {
        for (var i = 0; i < this.subscribers.length; i++) {
            if (this.subscribers[i] === callback) {
                delete(this.subscribers[i]);
            }
        }
    },
    publish:function (what) {
        for (var i = 0; i < this.subscribers.length; i++) {
            if (typeof this.subscribers[i] === 'function') {
                this.subscribers[i](what);
            }
        }
    },
    make:function (o) { // turns an object into a publisher
        for (var i in this) {
            o[i] = this[i];
            o.subscribers = [];
        }
    }
};

var blogger = {
    writeBlogPost:function () {
        var content = 'Today is ' + new Date();
        this.publish(content);
    }
};
var la_times = {
    newIssue:function () {
        var paper = 'Martians have landed on Earth!';
        this.publish(paper);
    }
};

observer.make(blogger);
observer.make(la_times);

var jack = {
    read:function (what) {
        console.log('I just read that ' + what)
    }
};
var jill = {
    gossip:function (what) {
        console.log('You didn\'t hear it from me, but ' + what)
    }
};

blogger.addSubscriber(jack.read);
blogger.addSubscriber(jill.gossip);
blogger.writeBlogPost();
blogger.removeSubscriber(jill.gossip);
blogger.writeBlogPost();
la_times.addSubscriber(jill.gossip);
la_times.newIssue();

避免创建多个被观察者对象,可以增加 "命名空间"

var Observable = {
    observers: []
  , addObserver: function(topic, observer) {
      this.observers[topic] || (this.observers[topic] = [])

      this.observers[topic].push(observer)
    }
  , removeObserver: function(topic, observer) {
      if (!this.observers[topic])
        return;

      var index = this.observers[topic].indexOf(observer)

      if (~index) {
        this.observers[topic].splice(index, 1)
      }
    }
  , notifyObservers: function(topic, message) {
      if (!this.observers[topic])
        return;

      for (var i = this.observers[topic].length - 1; i >= 0; i--) {
        this.observers[topic][i](message)
      };
    }
}

Observable.addObserver('cart', function(message){
  console.log("First observer message:" + message)
})

Observable.addObserver('notificatons', function(message){
  console.log("Second observer message:" + message)
})

Observable.notifyObservers('cart', 'test 1')
// First observer message:test 1

Observable.notifyObservers('notificatons', 'test 2')
// Second observer message:test 2

相关阅读

javascript-observer-publish-subscribe-pattern
深入理解JavaScript系列(32):设计模式之观察者模式
javascript-patterns-observer

javaScript 设计模式系列之一:观察者模式的更多相关文章

  1. javascript设计模式系列

    javascript设计模式系列   创建型: 1.抽象工厂模式(Abstract Factory) 2.构建者模式(Builder) 3.工厂方法模式(Factory Method) 4.原型模式( ...

  2. 【JavaScript设计模式系列---开篇预览】

    转:http://www.cnblogs.com/Darren_code/archive/2011/08/31/JavascripDesignPatterns.html 2011-08-31 23:5 ...

  3. Javascript设计模式系列三

    继承,一个类或对象继承另一个类或对象的三种方法.类式继承.原型式继承.掺元类. 一.类式继承,原型链.Extend函数. <script type="text/javascript&q ...

  4. Javascript设计模式系列二

    创建对象的基本模式,一.门户大开型,二.采用下划线来表示属性和方法的私用性,三.使用闭包来创建私用的成员. 一.门户大开型.只能提供公用成员.所有属性和方法都公开的.可访问的.这些共用属性都要使用th ...

  5. Javascript设计模式系列一

    定义类,方法赋值给prototype属性各种写法: 方法一: <script type="text/javascript"> ////定义Message类 var Me ...

  6. 【读书笔记】读《JavaScript设计模式》之观察者模式

    一.定义 在事件驱动的环境中,比如浏览器这种持续寻求用户关注的环境中,观察者模式(又名发布者-订阅者(publisher-subscripber)模式)是一种管理人与其任务之间的关系(确切地讲,是对象 ...

  7. C#设计模式系列:观察者模式(Observer)

    在软件构建过程中,需要为某些对象建立一种“通知依赖关系”,即一个对象的状态发生改变,所有的依赖对象都需要得到通知. 1.观察者模式简介 1.1>.定义 定义对象间的一种一对多的依赖关系,当一个对 ...

  8. Java设计模式系列之观察者模式

    观察者模式 Observer的定义 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象. 这个主题对象在状态上发生变化时,会通知所有观察者对象,让它们能够自动更新自己. 第一 ...

  9. javaScript 设计模式系列之二:适配器模式

    介绍 适配器模式将一个类的接口转接成用户所期待的,有助于避免大规模改写现有客户代码. In software engineering, the adapter pattern is a softwar ...

随机推荐

  1. C#控制台程序取得INSOYA视频区的视频的真实URL,视频标题,发布时间集合。

    准备工作 起因是因为这个网站:http://i.youku.com/kmsfan 这个是一个叫做冒险岛的游戏的资讯论坛,以前我经常在里面传视频,现在我不玩这个游戏了,但是很多玩家还是经常到我的网站里面 ...

  2. 《JavaScript Dom编程艺术》(第二版)

    第一章<JavaScript简史> 1.JavaScript是一种脚本语言,通常只能通过Web浏览器去完成一些操作而不能像普通意义上的程序那样独立运行,它需要由Web浏览器进行解释和执行. ...

  3. MODI与VB6

    作者:马健邮箱:stronghorse_mj@hotmail.com主页:http://www.comicer.com/stronghorse发布:2016.12.16 在我写的<用MODI O ...

  4. 数据库 SQL Server 到 MySQL 迁移方法总结

    最近接手一起老项目数据库 SQL Server 到 MySQL 的迁移.因此迁移前进行了一些调查和总结.下面是一些 SQL Server 到 MySQL 的迁移方法. 1. 使用 SQLyog 迁移 ...

  5. 【GPU编解码】GPU硬解码---DXVA

    前面介绍利用NVIDIA公司提供的CUVID库进行视频硬解码,下面将介绍利用DXVA进行硬解码. 一.DXVA介绍 DXVA是微软公司专门定制的视频加速规范,是一种接口规范.DXVA规范制定硬件加速解 ...

  6. 遍历Map集合的方法

    创建一个MAP的栗子: Map<String, Integer> tempMap = new HashMap<String, Integer>(); tempMap.put(& ...

  7. Html5 Canvas动画旋转的小方块;

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...

  8. 让你了解x86的中断

    COPY FROM:http://zhan.renren.com/qdlinux?gid=3602888498000980107&from=post&checked=true 研究li ...

  9. 函数 resize和reserve的区别

    reserve是容器预留空间,但在空间内不真正创建元素对象,所以在没有添加新的对象之前,不能引用容器内的元素.加入新的元素时,要调用push_back()/insert()函数. resize是改变容 ...

  10. java 知识 链接

    www.java2blog.com/2016/04/hashmap-in-java-with-examples.html HashMap全剖析 http://card.weibo.com/articl ...