大家好!本文介绍观察者模式及其在Javascript中的应用。

模式介绍

定义

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

类图及说明

Subject:主题\发布者

能够动态地增加、取消观察者。它负责管理观察者并通知观察者。

Observer:观察者\订阅者

观察者收到消息后,即进行update操作,对接收到的信息进行处理。

ConcreteSubject:具体的主题\发布者

定义主题自己的业务逻辑,同时定义对哪些事件进行通知。

ConcreteObserver:具体的观察者\订阅者

每个观察者在接受到消息后的处理反应时不同的,各个观察者有自己的处理逻辑。

应用场景

  • 当一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。
  • 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。

  • 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。

优点

  1. 支持简单的广播通信,自动通知所有已经订阅过的对象。
  2. 页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性。
  3. 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。

缺点

1、  松偶合导致代码关系不明显,有时可能难以理解。

2、  如果一个Subject被大量Observer订阅的话,在广播通知的时候可能会有效率问题。

观察者模式在Javascript中的应用

类图及说明

这里有两个变化:

  1. ConcreteSubject与Subject的继承关系改为委托关系。
  2. 删除了Observer基类,直接将观察者的update方法订阅到Subject的events数组中。

应用场景

  • 一个对象变化时触发多个对象变化
  • 一个对象调用其他对象的方法,而又不想与之耦合

示例

现在是找工作的季节,大家都在忙着找工作。大神可以单枪匹马秒杀各种offer,高富帅也有各种关系帮忙找工作。

让我们来看下高富帅是如何找工作的。

类图

代码

GaoFuShuai

function GaoFuShuai() {
this._wang = new Wang();
} GaoFuShuai.prototype.findJob = function () {
console.log("高富帅开始找工作了");
this._wang.help();
};

王哥

        function Wang() {
} Wang.prototype.help = function () {
console.log("高富帅找工作啊,王哥来助你");
}

场景

function main() {
var gaofushuai = new GaoFuShuai(); gaofushuai.findJob();
}

运行结果

分析

本设计有以下的缺点:

  1. 观察者可能不止一个,如果增加李哥、张哥等观察类,那就都要对应修改高富帅类,不符合开闭原则。
  2. 观察者可能不仅仅要观察高富帅的找工作情况,还要观察高富帅的上学、娱乐等情况,这样会有严重的耦合问题。

使用观察者模式

现在使用观察者模式来改进设计。

类图

代码

Subject

   (function () {
if (!Array.prototype.forEach) {
Array.prototype.forEach = function (fn, thisObj) {
var scope = thisObj || window;
for (var i = , j = this.length; i < j; ++i) {
fn.call(scope, this[i], i, this);
}
};
} if (!Array.prototype.filter) {
Array.prototype.filter = function (fn, thisObj) {
var scope = thisObj || window;
var a = [];
for (var i = , j = this.length; i < j; ++i) {
if (!fn.call(scope, this[i], i, this)) {
continue;
}
a.push(this[i]);
}
return a;
};
} var Subject = function () {
this._events = [];
} Subject.prototype = (function () {
return {
//订阅方法
subscribe: function (context, fn) {
if (arguments.length == ) {
this._events.push({ context: arguments[], fn: arguments[] });
}
else {
this._events.push(arguments[]);
}
},
//发布指定方法
publish: function (context, fn, args) {
var args = Array.prototype.slice.call(arguments, ); //获得函数参数
var _context = null;
var _fn = null; this._events.filter(function (el) {
if (el.context) {
_context = el.context;
_fn = el.fn;
}
else {
_context = context;
_fn = el;
} if (_fn === fn) {
return _fn;
}
}).forEach(function (el) { //指定方法可能有多个
el.apply(_context, args); //执行每个指定的方法
});
},
unSubscribe: function (fn) {
var _fn = null;
this._events = this._events.filter(function (el) {
if (el.fn) {
_fn = el.fn;
}
else {
_fn = el;
} if (_fn !== fn) {
return el;
}
});
},
//全部发布
publishAll: function (context, args) {
var args = Array.prototype.slice.call(arguments, ); //获得函数参数
var _context = null;
var _fn = null; this._events.forEach(function (el) {
if (el.context) {
_context = el.context;
_fn = el.fn;
}
else {
_context = context;
_fn = el;
} _fn.apply(_context, args); //执行每个指定的方法
});
},
dispose: function () {
this._events = [];
}
}
})(); window.Subject = Subject;
})();

GaoFuShuai

function GaoFuShuai() {
} GaoFuShuai.prototype.findJob = function () {
console.log("高富帅开始找工作了");
window.subject.publishAll(null, "帮忙找工作");
};
GaoFuShuai.prototype.haveFun = function () {
console.log("高富帅开始娱乐了");
window.subject.publishAll(null, "帮忙找乐子");
};

王哥

function Wang() {
} Wang.prototype.help = function (actionStr) {
console.log("王哥" + actionStr);
};

李哥

function Li() {
} Li.prototype.help = function (actionStr) {
console.log("李哥" + actionStr);
};

场景

function main() {
var wang = new Wang(),
li = new Li(),
gaofushuai = new GaoFuShuai(); window.subject = new Subject();
window.subject.subscribe(wang.help);
window.subject.subscribe(li.help); gaofushuai.findJob();
gaofushuai.haveFun();
}

运行结果

分析

这样就符合开闭原则了,被观察者与观察者也不再直接耦合了。如果想继续增加观察者,则只需对应修改main即可,被观察者GaoFuShuai类不用修改。

参考资料

深入理解JavaScript系列(32):设计模式之观察者模式《设计模式之禅》

Javascript设计模式之我见:观察者模式的更多相关文章

  1. Javascript设计模式之我见:状态模式

    大家好!本文介绍状态模式及其在Javascript中的应用. 模式介绍 定义 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式主要解决的是控制一个对象状态的条件表达式 ...

  2. Javascript设计模式之我见:迭代器模式

    大家好!本文介绍迭代器模式及其在Javascript中的应用. 模式介绍 定义 提供一种方法顺序一个聚合对象中各个元素,而又不暴露该对象内部表示. 类图及说明 Iterator抽象迭代器 抽象迭代器负 ...

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

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

  4. [转] 浅析JavaScript设计模式——发布-订阅/观察者模式

    前一段时间一直在写CSS3的文章 一直都没写设计模式 今天来写写大名鼎鼎观察者模式 先画张图 观察者模式的理解 我觉得还是发布-订阅模式的叫法更容易我们理解 (不过也有的书上认为它们是两种模式……)  ...

  5. JavaScript设计模式与开发实践 - 观察者模式

    概述 观察者模式又叫发布 - 订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个目标对象(为了方便理解,以下将观察者对象叫做订阅者,将目标对象叫做 ...

  6. JavaScript设计模式之观察者模式(学习笔记)

    设计模式(Design Pattern)对于软件开发来说其重要性不言而喻,代码可复用.可维护.可扩展一直都是软件工程中的追求!对于我一个学javascript的人来说,理解设计模式似乎有些困难,对仅切 ...

  7. 再起航,我的学习笔记之JavaScript设计模式18(观察者模式)

    观察者模式 观察者模式(Observer): 又被称为发布-订阅者模式或消息机制,定义了一种依赖关系,解决了主体对象与观察者之间功能的耦合. 创建一个观察者对象 首先我们创建一个闭包对象,让其在页面加 ...

  8. [转] JavaScript设计模式之发布-订阅模式(观察者模式)-Part1

    <JavaScript设计模式与开发实践>读书笔记. 发布-订阅模式又叫观察者模式,它定义了对象之间的一种一对多的依赖关系.当一个对象的状态发生改变时,所有依赖它的对象都将得到通知. 例如 ...

  9. 《JavaScript设计模式 张》整理

    最近在研读另外一本关于设计模式的书<JavaScript设计模式>,这本书中描述了更多的设计模式. 一.创建型设计模式 包括简单工厂.工厂方法.抽象工厂.建造者.原型和单例模式. 1)简单 ...

随机推荐

  1. 配置 Sublime Text 3 作为Python R LaTeX Markdown IDE

    配置 Sublime Text 3 作为Python R LaTeX Markdown IDE 配置 Sublime Text 3 作为Python IDE IDE的基本功能:代码提醒.补全:编译文件 ...

  2. Android IPC机制(三)使用AIDL实现跨进程方法调用

    上一篇文章中我们介绍了使用Messenger来进行进程间通信的方法,但是我们能发现Messenger是以串行的方式来处理客户端发来的信息,如果有大量的消息发到服务端,服务端仍然一个一个的处理再响应客户 ...

  3. 性能测试 查看Android APP 帧数FPS的方法

    (下述需要先安装eclipse,不然无法抓包) 1.保证手机与PC连接是正常的 2.打开手机“设置”→“开发者选项”(没有开发者选项就点击“关于手机”“版本号”连续点击就会出现开发者选项了).找到监控 ...

  4. android修改getprop读取到的ro.build.fingerprint属性

    在build/tools/buildinfo.sh中定义ro.build.fingerprint=$BUILD_FINGERPRINT. 然后在build/core/Makefile中给BUILD_F ...

  5. javascript的介绍、javascirpt的存在形式、javascript注释

    本文内容: javascript的介绍 javascirpt的存在形式 javascript注释 javascript的介绍: javascript是一种解释性脚本语言 它的用途是给html网页增加动 ...

  6. Python PEP-8编码风格指南中文版

    #PEP 8 – Python编码风格指南 PEP: 8 Title: Style Guide for Python Code Author: Guido van Rossum , Barry War ...

  7. 【爬坑】远程连接 MySQL 失败

    问题描述 远程连接 MySQL 服务器失败 报以下错误 host 192.168.23.1 is not allowed to connect to mysql server 解决方案 在服务器端打开 ...

  8. 代码管理工具:Git 和 Svn 的简单操作

    1. git 先注册git config --global user.name "name" git config --global user.email "email& ...

  9. June. 21 2018, Week 25th. Thursday

    Summertime is always the best of what might be. 万物最美的一面,总在夏季展现. From Charles Bowden. It was June, an ...

  10. Java(使用JNA)调用DLL库与C#调用DLL库的对比

    前言:在项目中经常使用DLL库对硬件进行操作,在发卡过程中使用频率尤为多,今天就Java与C#中调用DLL库的使用区别做一个介绍,本文着重具体的代码编写,具体过程看以下代码. 前提条件: 笔者已经封装 ...