观察者模式主要应用于对象之间一对多的依赖关系,当一个对象发生改变时,多个对该对象有依赖的其他对象也会跟着做出相应改变,这就非常适合用观察者模式来实现。使用观察者模式可以根据需要增加或删除对象,解决一对多对象间的耦合关系,使程序更易于扩展和维护。

基础知识

观察者模式定义了对象间的一种一对多依赖关系,每当一个对象发生改变时,其相关依赖对象皆得到通知并被进行相应的改变。观察者模式又叫做发布-订阅模式。生活中有很多类似的关系,比如微信公众号订阅,多个读者订阅一个微信公众号,一旦公众号有更新,多个读者都会收到更新,而这种情况在应用程序中也非常常见,js绑定各种事件本质上就是观察者模式的实现。
观察者模式是一个非常有用的设计模式,它主要有两个角色组成:
(1)目标对象:作为一对多关系中的一,可以用来管理观察者的增加和删除
(2)观察者对象:观察目标对象,一旦目标发生改变则做出相应的反应

观察者模式的实现

基本示例

在Web开发中,我们经常遇到这种情况,ajax请求数据后,要同时更新数据到页面的不同部分中,这种情况我们可以最直接的在ajax的回调中更新页面,但是如果要更新的位置很多,我们就要去修改回调函数,这样代码的维护性和扩张性不高,这种情况下,我们就可以用观察者模式来实现。
首先,需要一个最基本的目标对象,我们定义如下:

 function Subject() {
this.observers = [];
}
Subject.prototype = {
constructor: Subject,
subscribe: function (fn) {
this.observers.push(fn);
return this;
},
unsubscribe: function (fn) {
this.observers = this.observers.filter(function (item) {
if (item !== fn) {
return item;
}
});
return this;
},
fire: function (data, context) {
this.observers.forEach(function (item) {
item.call(context, data);
});
return this;
}
};

目标对象Subject中有一个数组,这个数组保存观察者列表,而目标对象提供三个方法:观察对象,取消观察对象,触发对象更新。
我们通过subscribe方法增加观察者,保存到observers数组中,如果有需要可以通过unsubscribe方法取消订阅,然后更新数据时调用fire方法触发,从而通知各个观察者进行相应处理。
假设我们页面有一个主视图和一个侧视图,两个视图都要进行相应的修改,我们可以定义两个对象如下:

 function SideView() { }
SideView.prototype.render = function (data) {
console.log("Side data:" + data);
}
function MainView() { }
MainView.prototype.render = function (data) {
console.log("MainView data:" + data);
}

上面代码定义了两个对象,分别为侧视图和主视图,两个对象都有相应的渲染页面的方法render,然后我们将两个方法添加到观察者列表中。

 var subject = new Subject();
var sideView = new SideView();
var mainView = new MainView(); subject.subscribe(sideView.render)
subject.subscribe(mainView.render);
subject.fire("test");

通过调用fire方法,传入“test”,从而触发两个render函数。从这段代码中,我们可以很轻松地通过subscribe来添加观察者对象,而不必每次都去修改fire方法。

jQuery中的观察者模式

jQuery中实现观察者模式非常方便,简短的几句代码就可以实现

         (function ($) {
var obj = $({});
$.subscribe = function () {
obj.on.apply(obj, arguments);
}
$.unsubscribe = function () {
obj.off.apply(obj, arguments);
}
$.fire = function () {
obj.trigger.apply(obj, arguments);
}
})(jQuery);

在jQuery中,通过on方法来绑定事件,off来移除事件,trigger来触发事件,本质上就是一种观察者模式。上面代码中,我们通过一个obj对象来保存观察者对象,我们只要像平时绑定事件一样使用就可以,如下:

 $.subscribe("render", function () {
console.log("test");
})
$.subscribe("render", function () {
console.log("test2");
})
$.fire("render");

这段代码分别输出test和test2.我们绑定了两个处理函数到render上,然后通过fire触发render事件,这就实现了观察者模式一对多依赖的特点。

总结

观察者模式是一种很常用的设计模式,因为我们的应用程序中涉及到依赖关系的非常多。常见的比如消息通知,向用户发送一个消息需要同时通知到站内信,邮件,短信等多种消息,这种一对多的情况非常适合使用观察者模式来实现。使用观察者模式的关键是在于理清目标对象和观察者对象,目标对象通过一个数组对观察者对象进行管理,更新数据的时候再循环调用观察者对象,从而实现观察者模式。

原文地址:http://luopq.com/2015/11/24/design-pattern-observer/

Javascript设计模式理论与实战:观察者模式的更多相关文章

  1. Javascript设计模式理论与实战:工厂方法模式

    本文从简单工厂模式的缺点说起,引入工厂方法模式,介绍的工厂方法模式的基本知识,实现要点和应用场景,最后举例进行说明工厂方法模式的应用.在之前的<Javascript设计模式理论与实战:简单工厂模 ...

  2. Javascript设计模式理论与实战:单例模式

    在Javascript中,单例模式是一种最基本又经常用到的设计模式,可能在不经意间就用到了单例模式. 本文将从最基础的理论开始,讲述单例模式的基本概念和实现,最后用一个例子来讲述单例模式的应用. 理论 ...

  3. Javascript设计模式理论与实战:桥接模式

    桥接模式将抽象部分与实现部分分离开来,使两者都可以独立的变化,并且可以一起和谐地工作.抽象部分和实现部分都可以独立的变化而不会互相影响,降低了代码的耦合性,提高了代码的扩展性. 基本理论 桥接模式定义 ...

  4. Javascript设计模式理论与实战:简单工厂模式

    通常我们创建对象最常规的方法就是使用new关键字调用构造函数,这会导致对象之间的依赖性.工厂模式是一种有助于消除类之间依赖性的设计模式,它使用一个方法来决定要实例化哪一个类.本文详细介绍了简单工厂模式 ...

  5. Javascript设计模式理论与实战:状态模式

    在软件开发中,很大部分时候就是操作数据,而不同数据下展示的结果我们将其抽象出来称为状态,我们平时开发时本质上就是对应用程序的各种状态进行切换并作出相应处理.状态模式就是一种适合多种状态场景下的设计模式 ...

  6. Javascript设计模式理论与实战:享元模式

    享元模式不同于一般的设计模式,它主要用来优化程序的性能,它最适合解决大量类似的对象而产生的性能问题.享元模式通过分析应用程序的对象,将其解析为内在数据和外在数据,减少对象的数量,从而提高应用程序的性能 ...

  7. Javascript设计模式理论与实战:组合模式

    我们平时开发过程中,一定会遇到这种情况:同时处理简单对象和由简单对象组成的复杂对象,这些简单对象和复杂对象会组合成树形结构,在客户端对其处理的时候要保持一致性.比如电商网站中的产品订单,每一张产品订单 ...

  8. Javascript设计模式理论与实战:适配器模式

    有的时候在开发过程中,我们会发现,客户端需要的接口和提供的接口发生不兼容的问题.由于特殊的原因我们无法修改客户端接口.在这种情况下,我们需要适配现有接口和不兼容的类,这就要提到适配器模式.通过适配器, ...

  9. 前端读者 | Javascript设计模式理论与实战:状态模式

    本文来自 @狼狼的蓝胖子:链接:http://luopq.com/2015/11/25/design-pattern-state/ 在软件开发中,很大部分时候就是操作数据,而不同数据下展示的结果我们将 ...

随机推荐

  1. springmvc后台生成验证码

    url http://localhost:8080/admin/getCode http://localhost:8080/admin/checkCode controller package com ...

  2. 前端基础之Bootstrap

    1. 页面加载完之后才执行的JS代码        1. DOM方式            window.onload = function(){}        2. jQuery方式       ...

  3. 2.Add Two Numbers (List)

    You are given two linked lists representing two non-negative numbers. The digits are stored in rever ...

  4. Maven项目的拆分与聚合

    ---------------------siwuxie095                                     Maven 项目的拆分与聚合         1.对已有的 Ma ...

  5. java里的静态成员变量是放在了堆内存还是栈内存

    转自http://bbs.csdn.NET/topics/370001490 堆区: 1.存储的全部是对象,每个对象都包含一个与之对应的class的信息.(class的目的是得到操作指令) 2.jvm ...

  6. ubuntu 操作系统的目录结构

    Ubuntu 系统的目录众多,并且 Ubuntu 系统是不分 C 盘.D 盘等的,但是所有的目录都是在/目录下面的. 一./:根目录,是所有目录的绝对路径的起始点,Ubuntu 中的所有文件和目录都在 ...

  7. linux 下 php 安装 libevent

    一.安装libevent库 1.到libevent官网下载安装源码 http://libevent.org/ 如:libevent-2.0.22-stable.tar.gz 2.解压源码包 > ...

  8. win下php的memcached的安装与使用

    1.memcache的php扩展与memcached服务器的区别? php要操作memcached就必须要安装memcache的扩展, 在http://windows.php.net/download ...

  9. python性能测试大致计划

      hi guy: 如果注意到创建时间,那就对了.这份文章,是我学习Python一个月以后动手写的.   写下这份计划以后,只完成了第一步,其中磕磕绊绊编写代码的过程,很大一部分时间是完全用txt写的 ...

  10. qstring转string

    Qt的QString功能丰富,对非英语语言的支持也不是问题,但支持得不够直接.例如,像 1 QString str("死亡使者赛维"); 这样直接用带中文的字符串进行构造,那么用Q ...