设计模式——观察者模式与订阅-发布模式
2018-11-13 本文已影响12人
BULL_DEBUG
首先,个人觉得观察者模式和订阅-发布模式是有区别的(一些文章会认为观察者模式即为订阅-发布模式),然后分别介绍一下这两种模式以及两种模式的区别。
先列出两者区别
观察者模式
观察者模式 在软件设计中是一个对象,维护一个依赖列表,当任何状态发生改变自动通知它们。
比较概念的解释是,目标和观察者是基类,目标提供维护观察者的一系列方法,观察者提供更新接口。具体观察者和具体目标继承各自的基类,然后具体观察者把自己注册到具体目标里,在具体目标发生变化时候,调度观察者的更新方法。
比如有个“天气中心”的具体目标A,专门监听天气变化,而有个显示天气的界面的观察者B,B就把自己注册到A里,当A触发天气变化,就调度B的更新方法,并带上自己的上下文。
image订阅-发布模式
在发布-订阅模式,消息的发送方,叫做发布者(publishers),消息不会直接发送给特定的接收者,叫做订阅者。
意思就是发布者和订阅者不知道对方的存在。需要一个第三方组件,叫做信息中介,它将订阅者和发布者串联起来,它过滤和分配所有输入的消息。换句话说,发布-订阅模式用来处理不同系统组件的信息交流,即使这些组件不知道对方的存在。
比较概念的解释是,订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度订阅者注册到调度中心的处理代码。
比如有个界面是实时显示天气,它就订阅天气事件(注册到调度中心,包括处理程序),当天气变化时(定时获取数据),就作为发布者发布天气信息到调度中心,调度中心就调度订阅者的天气处理程序。
image附录
观察者模式实现代码(JavaScript版):
<script>
/*
观察者模式
*/
// 观察者列表 定义一个 ObserverList 类方便管理多个数据
function ObserverList() {
this.observerList = [];
}
// 添加到观察者列表
ObserverList.prototype.add = function(obj) {
return this.observerList.push(obj);
}
// 观察者列表长度
ObserverList.prototype.count = function () {
return this.observerList.length;
}
// 获取当前观察者
ObserverList.prototype.get = function (index) {
if (index > -1 && index < this.observerList.length) {
return this.observerList[ index ];
}
}
// 检测是否在列表中
ObserverList.prototype.indexOf = function(obj, startIndex) {
var i = startIndex;
var len = this.observerList.length;
while (i < len) {
if ( this.observerList[i] === obj) {
return i;
}
i ++;
}
return -1;
}
// 取消订阅
ObserverList.prototype.removeAt = function (index) {
this.observerList.splice(index, 1);
}
// 被观察目标
function Subject () {
this.observers = new ObserverList();
}
Subject.prototype.addObserver = function (observer) {
this.observers.add(observer);
}
Subject.prototype.removeObserver = function (observer) {
this.observers.removeAt(this.observers.indexOf(observer, 0));
}
Subject.prototype.notify = function (context) {
var observerCount = this.observers.count();
for (var i = 0; i < observerCount; i++) {
this.observers.get(i).update(context)
}
}
// 定义一个 Observer 类作为观察者
function Observer(id) {
this.id = id;
}
Observer.prototype.update = function(data) {
console.log(data + '_' + this.id);
var content = document.getElementById("content");
content.innerHTML = '<div>' + data + '_' + this.id + '</div>'
}
var data = new Subject();
// 让多个观察者对象同时监听某一个主题对象,这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能自动更新自己。
var abserver1 = new Observer(1); // 观察者1
var abserver2 = new Observer(2); // 观察者2
var abserver3 = new Observer(3); // 观察者3
data.addObserver(abserver1);
data.addObserver(abserver2);
data.addObserver(abserver3);
data.removeObserver(abserver3);
data.notify('ccccc');
</script>
订阅-发布模式
<script>
// 定义DataHub作为发布者
function DataHub() {};
DataHub.prototype.notify = function(url, callback) {
callback(url);
}
// DownloadManager类作为事件通道
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);
var downloadTask2 = downloadManager.subscribe('dataReady2', dataLoader);
console.log(downloadTask1);
console.log(downloadTask2);
// 数据打包完成了
dataHub.notify('http://somedomain.someaddress', function(url) {
downloadManager.publish('dataReady', url);
downloadManager.publish('dataReady2', url)
});
</script>