大家好!本文介绍观察者模式及其在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. uni-app 父组件引用子组件时怎么调用子组件的方法

    1.写一个简单的子组件main/index.vue: <template> <view> </view> </template> <script& ...

  2. springboot 学习之路 4(日志输出)

    目录:[持续更新.....] spring 部分常用注解 spring boot 学习之路1(简单入门) spring boot 学习之路2(注解介绍) spring boot 学习之路3( 集成my ...

  3. ChakraCore/JSRT使用问题汇总

    QQ交流群:523723780(ChakraCore) ChakraCore是什么? 一个微软开源的,用于Windows IE/Edge内核的高效JS脚本引擎. 前不久微软开源了ChakraCore, ...

  4. [转] Scala Try 与错误处理

    一.概述 当你在尝试一门新的语言时,可能不会过于关注程序出错的问题, 但当真的去创造可用的代码时,就不能再忽视代码中的可能产生的错误和异常了. 鉴于各种各样的原因,人们往往低估了语言对错误处理支持程度 ...

  5. python之restful api(flask)获取数据

    需要用到谷歌浏览器的扩展程序 Advanced Rest Client进行模拟请求 1.直接上代码 from flask import Flask from flask import request ...

  6. 监控mysql主从同步

    1,昨天看到shell一道面试题,需求如下: 监控MySQL主从同步是否异常,如果异常,则发送短信或者邮件给管理员.提示:如果没主从同步环境,可以用下面文本放到文件里读取来模拟:阶段1:开发一个守护进 ...

  7. peewee基本操作

    本文将简单的介绍Peewee,一览它的主要特点,主要涉及到: 模型定义 存储数据 检索数据 注意:如果你偏爱稍微高级一点的教程, peewee建立类似twitter应用 是一篇结合Flask框架与pe ...

  8. IOS UIWebView用法

    转自猫猫小屋 IOS webview控件使用简介(一) IOS webview控件使用简介(二)–加载本地html

  9. ArcGIS Server10.2 集群部署注意事项

    不接触Server很久了,最近一个省级项目需要提交一个部署方案,由于是省级系统,数据.服务数量都较大,需要考虑采用Server集群的方式来实现.在网上搜罗了以下Server集群的资料,按照步骤一步步来 ...

  10. MySQL 8.0.11安装配置

    官网地址:https://dev.mysql.com/downloads/mysql/ 我这里是RHEL6.5的系统,因此选择RedHat 6 x86,64bit操作系统---下载第一个RPM Bun ...