1 概念解释:
(1)观察者设计模式
标准解释:
1
| 观察者模式: 是一种设计模式,在软件设计中往往是一个对象,维护一个依赖列表,当任何状态发生改变自动通知它们。
|
来源:维基百科
图示:
1 2 3 4 5
| ╭─────────────╮ Fire Event ╭──────────────╮ │ │─────────────>│ │ │ Subject │ │ Observer │ │ │<─────────────│ │ ╰─────────────╯ Subscribe ╰──────────────╯
|
白话讲解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 假设你想去xx公司应聘前端工程师,但xx公司hr告知你目前没有岗位空缺,你向留下了联系方式,让其在有岗位空缺时,通知你。当然公司还会通知同样已留下联系方式的其他候选者。
Observers(观察者): 候选人
Subject(被观察者): xx公司
Fire Event (事件): xx公司有了新前端空缺
Subscribe(订阅): 候选人将联系方式留给了xx公司
Notify (通知) : xx公司向候选人致电
观察者模式运行流程:
Observers -> Subscribe -> Subject -> Fire Event -> Notify -> Observers
|
(2)发布订阅模式
标准解释:
1 2 3 4 5
| 发布订阅模式属于广义上的观察者模式,发布-订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(如果有的话)存在。
发布订阅模式是最常用的一种观察者模式的实现,并且从解耦和重用角度来看,更优于典型的观察者模式
|
来源:维基百科
图示:
1 2 3 4 5
| ╭─────────────╮ ╭───────────────╮ Fire Event ╭──────────────╮ │ │ Publish Event │ │───────────────>│ │ │ Publisher │────────────────>│ Event Channel │ │ Subscriber │ │ │ │ │<───────────────│ │ ╰─────────────╯ ╰───────────────╯ Subscribe ╰──────────────╯
|
白话讲解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| - 你想成为一个前端工程师,但目前没有明确目标,你可以联系了猎头让其有前端相关岗位时通知你。
- 猎头可能与多家公司都有合作,这些公司发布职位会告知猎头
- 这个过程xx公司,只需要根据自身情况,只需要根据自己实际的岗位情况,告知猎头自己需要什么岗位的候选人,无需知道有哪些具体候选人,候选人在订阅过程中,只是向猎头公司订阅了有前端相关岗位时通知自己
Publisher(发布者): xx公司
Publish Event(发布事件): 发布职位
Event Channel (事件通道): 猎头公司
Fire Event(事件): xx公司发布职位后,猎头公司根据职位发送联系相应岗位候选人
Subscriber(订阅者): 候选人
Subscribe(订阅): 候选人将联系方式留给hr
运行流程:
Subscriber -> Subscribe -> Event Channel
Publisher -> Publish Event -> Event Channel -> Fire Event -> Subscriber
|
2 观察者模式和发布订阅模式有什么不同:
发布订阅模式属于广义上的观察者模式。发布订阅模式是最常用的一种观察者模式的实现,并且从解耦和重用角度来看,更优于典型的观察者模式。
区别:
在观察者模式中,观察者需要直接订阅目标事件;在目标发出内容改变的事件后,直接接收事件并作出响应
- 在发布订阅模式中,发布者和订阅者之间多了一个发布通道;一方面从发布者接收事件,另一方面向订阅者发布事件;订阅者需要从事件通道订阅事件
以此避免发布者和订阅者之间产生依赖关系。
3. 观察者模式代码实现
使用观察者模式的好处:
- 支持简单的广播通信,自动通知所有已经订阅过的对象。
- 页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性。
- 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。
最简单观察者模式在js中的运用
1 2 3
| document.body.addEventListener('click', function () { console.log('body 被观察') });
|
观察者模式实现数据打包下载功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
|
function DownloadTask(id) { this.id = id; this.loaded = false; this.url = null; }
DownloadTask.prototype.finish = function(url) { this.loaded = true; this.url = url; console.log('Task ' + this.id + ' load data from ' + url); };
function DataHub() { this.downloadTasks = []; }
DataHub.prototype.addDownloadTask = function(downloadTask) { this.downloadTasks.push(downloadTask); };
DataHub.prototype.removeDownloadTask = function(downloadTask) { this.downloadTasks.filter(function (item) { if (item === downloadTask) {
} }) };
DataHub.prototype.notify = function(url) { const downloadTaskCount = this.downloadTasks.length; for (var i = 0; i < downloadTaskCount; i++) { this.downloadTasks[i].finish(url); } };
var d = new DataHub();
var task1 = new DownloadTask(1); var task2 = new DownloadTask(2);
d.addDownloadTask(task1); d.addDownloadTask(task2);
d.removeDownloadTask(task1);
d.notify('http://somedomain.someaddress');
|
4. 发布订阅代码实现
1
| 发布订阅模式是最常用的一种观察者模式的实现,并且从解耦和重用角度来看,更优于典型的观察者模式
|
1
| 在观察者模式中,观察者需要直接订阅目标事件;在目标发出内容改变的事件后,直接接收事件并作出响应
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| function Observer () { this.fnList = []; }
Observer.prototype = { subscribe: function (fn) { this.fnList.push(fn); },
unsubscribe: function (fn) { this.fnList = this.fnList.filter(function (el) { if (el !== fn){ return el; } }) }, notify: function (p, thisObj) { var scope = thisObj || window; this.fnList.forEach( function (el) { el.call(scope, o); } ); console.log(this.fnList); }
};
var o = new Observer;
var f1 = function (data) { console.log('Robbin: ' + data + ', 赶紧干活了!'); };
var f2 = function (data) { console.log('Randall: ' + data + ', 找他加点工资去!'); };
o.subscribe(f1); o.subscribe(f2);
o.notify("Tom回来了!");
o.unsubscribe(f1);
o.notify("Tom回来了!");
|
发布定远模式实现数据打包下载功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
function DataHub() {}
DataHub.prototype.notify = function(url, callback) { callback(url); }
function DownloadManager() { this.events = {}; this.uId = -1; }
DownloadManager.prototype.publish = function(eventType, url) { if (!this.events[eventType]) { return false; } var subscribers = this.events[eventType], count = subscribers ? subscribers.length : 0; while (count--) { var subscriber = subscribers[count]; subscriber.handler(eventType, subscriber.taskId, url); } }
DownloadManager.prototype.subscribe = function(eventType, handler) { if (!this.events[eventType]) { this.events[eventType] = []; } var taskId = (++this.uId).toString(); this.events[eventType].push({ taskId: taskId, handler: handler });
return taskId; }
var dataHub = new DataHub();
var downloadManager = new DownloadManager();
var dataLoader = function(eventType, taskId, url) { console.log('Task ' + taskId + ' load data from ' + url); }
var downloadTask1 = downloadManager.subscribe('dataReady', dataLoader);
dataHub.notify('http://somedomain.someaddress', function(url){ downloadManager.publish('dataReady', url); });
|