正文
观察者模式
观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。
使用观察者模式的好处:
-
支持简单的广播通信,自动通知所有已经订阅过的对象。
-
页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性。
-
目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。
理解的第一阶段
我们先用一个通俗的情景来比喻一下,就拿 航班 与 航站楼 之间的关系:
图上显示的是 飞机1在从 北京 飞往 南京, 北京航站楼A 和 南京航站楼B 都需要关注 飞机1 的信息,
而 飞机1 在位置变化 之后也要及时的 通知 北京航站楼A 和 南京航站楼。
(其实可以看出来,这里 航站楼是被动的接受 飞机是主动的发出变化)
所以,我们可以写一个简单的 观察/订阅模式
function Observer () {
// 数组存放多个观察者
this.fns = [];
}
Observer.prototype = {
// 订阅 (飞机信息)
sub: function (fn) {
this.fns.push(fn);
},
// 退订 (飞机信息)
unsub: function (fn) {
this.fns = this.fns.filter(function(el) {
if (el !== fn) {
return el;
}
})
},
// 发布 (飞机信息变化时)
publish: function (o, thisObj) {
var scope = thisObj || window;
this.fns.forEach(function(el) {
el.call(scope, o);
});
}
}
var ob = new Observer;
初始化 飞机1 的地理位置
var location = '北京';
定义 2 个航站楼的行为
// 北京 航站楼A - 观察者
var hangzhanlouBeijing = function (data) {
console.log('北京航站楼 收到了数据变化的通知,我知道现在 飞机1 的位置了,是在:' +data);
// 做一些操作
};
// 南京 航站楼B - 观察者
var hangzhanlouNanjing= function (data) {
console.log('南京航站楼 收到了数据变化的通知,我知道现在 飞机1 的位置了,是在:' +data + ',我这就准备接机');
// 做一些操作
};
南京和北京航站楼开始订阅(注册),将要关注 飞机1 的地址位置
// 订阅 (飞机地理位置的变化)
ob.sub(hangzhanlouBeijing);
ob.sub(hangzhanlouNanjing);
飞机1 经过一段时间的飞行 从 北京 到了 南京 ,这时候地理位置发生了变化
// 经过一些 操作 导致 location 发生了变化
location = '南京';
重要
然后
检查(校验)
location(地址位置) 是否发生变化,如果发生变化,就进行通知
// 数据 location 发生变化(现在的值 不等于 原来的值)
if (location!== '北京') {
// 发布 数据变化了(确认地址位置变了 ),广播 所有订阅(关注) 飞机1 的观察者(航站楼)
ob.update(location);
}
输出
北京航站楼 收到了数据变化的通知,我知道现在 飞机1 的位置了,是在: 南京
南京航站楼 收到了数据变化的通知,我知道现在 飞机1 的位置了,是在: 南京,我这就准备接机
理解的第二阶段
那这样情况就比较复杂了
学术一点的意思就是:要让多个对象都具有观察者模式(让
多个
观察者
可以关注
多个
自己想关注的
观察对象
)。
这时候分析下,相比较之前有什么变化
-
首先,存放
观察者(航站楼)
的数组 的
个数
不能固定,且
类别/附属
应该 与
观察对象
关联挂钩
-
再者,
观察对象
的
被观察属性
也不应该固定(之前只关注了 飞机1 的 location ,比如,我们可以观察 飞机 的
地理位置
,同样也可以关注 飞机 的
海拔高度
等信息)
用图表示 应该就是下面这种
数据情景
所以这里 我们定义的一个通用函数 ,它应该具备 上述的 一些特性
-
多个观察者
-
多个观察者对象的多个属性
-
给 他们添加 订阅,取消订阅,发布的方法
也就是说:每个观察者对象,都独立维护一套独立的观察者模式。
根据需要,我们抽象出下面 这样一个通用的函数:
//通用代码
var observer = {
//订阅
addSubscriber: function (callback) {
this.subscribers[this.subscribers.length] = callback;
},
//退订
removeSubscriber: function (callback) {
for (var i = 0; i < this.subscribers.length; i++) {
if (this.subscribers[i] === callback) {
delete (this.subscribers[i]);
}
}
},
//发布
publish: function (what) {
for (var i = 0; i < this.subscribers.length; i++) {
if (typeof this.subscribers[i] === 'function') {
this.subscribers[i](what);
}
}
},
// 通过遍历,给 观察者对象 o 添加 观察者容器,订阅,退订,发布
make: function (o) {
for (var i in this) {
o[i] = this[i];
// 每个 观察者对象 都维护自身的一个 观察者 数组
o.subscribers = [];
}
}
};
OK,已经抽象出了 具体的 范式,下面就结合具体情景来看下看是否符合
下面 先分别定义 飞机1,飞机2 这两个观察者对象的 多个属性(地理位置 和 海拔)
var plan1 = {
location: '北京',
height: '200km'
}
var plan2 = {
location: '云南',
height: '100km'
}
再,分别定义 观察者 的行为(航站楼1,航站楼2 分别关注 飞机的运行状态)