Open zhaoqize opened 6 years ago
观察者模式又叫发布订阅模式(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 具有了 观察者模式,如果再来一架 飞机2 也是从 北京 飞往 南京 的呢?很明显,我们也需要知道 飞机2 的地理位置信息
情况二: 还有假如 北京航站楼 只关注 飞机1 的变化,而不关注 飞机2 的变化,同理, 南京航站楼 只关注 _飞机2_的变化, 而不关注 飞机1 的变化
那这样情况就比较复杂了
学术一点的意思就是:要让多个对象都具有观察者模式(让 多个 观察者 可以关注 多个 自己想关注的 观察对象)。
观察者
观察对象
这时候分析下,相比较之前有什么变化
用图表示 应该就是下面这种 数据情景
所以这里 我们定义的一个通用函数 ,它应该具备 上述的 一些特性
也就是说:每个观察者对象,都独立维护一套独立的观察者模式。
根据需要,我们抽象出下面 这样一个通用的函数:
//通用代码 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 分别关注 飞机的运行状态)
var hangzhanlou1 = function hangzhanlou1(cb) { console.log('航站楼1 收到了 我关注的 飞机 的运行状态发生了改变...') // 订阅的对象发生了变化 , 观察者 做一些自己想做的事... cb(); } var hangzhanlou2 = function hangzhanlou2 (cb) { console.log('航站楼2 收到了 我关注的 飞机 的运行状态发生了改变...') // 订阅的对象发生了变化 , 观察者 做一些自己想做的事... cb(); }
这里,航站楼1 可以 订阅 飞机1,也可以订阅 飞机2 ,也可以都订阅
同样,航站楼1 可以订阅 飞机的 位置,也可以订阅飞机的 海拔, 也可以都订阅
给 飞机1,飞机2的 地理位置,海拔高度 都 添加 观察者模式
observer.make(plan1); observer.make(plan2);
添加 观察者 hangzhanlou1
plan1.addSubscriber(hangzhanlou1)
飞机1 经过飞行,判断 关注的信息是否发生了变化
这里判断是伪代码,但是这个判断的逻辑非常!非常!非常!的重要!如果有时间,这个后续会展开讲解,因为这里是另外一个核心内容,diff变化,从而控制View层的渲染!
// 这里为了演示方便,默认判断 地理,海拔都变化了(其实也可以判断 观察者某一个 属性) function checkChange(val, oldVal) { if (val !== oldVal) { plan1.publish(function(){ console.log('飞机1 现在位置是上海,海拔是100km'); }) } } // 航站楼1 收到了 我关注的 飞机 的运行状态发生了改变... // 飞机1 现在位置是上海,海拔是100km
plan1.addSubscriber(hangzhanlou2)
同样判断状态的改变,进行发布
function checkChange(val, oldVal) { if (val !== oldVal) { plan1.publish(function(){ console.log('飞机1 现在位置是太原,海拔是120km'); }) } } // 航站楼1 收到了 我关注的 飞机 的运行状态发生了改变... // 飞机1 现在位置是太原,海拔是120km // 航站楼2 收到了 我关注的 飞机 的运行状态发生了改变... // 飞机1 现在位置是太原,海拔是120km
plan2.addSubscriber(hangzhanlou1)
function checkChange1(val, oldVal) { if (val !== oldVal) { plan1.publish(function(){ console.log('飞机1 现在位置是青海,海拔是300km'); }) } } // 航站楼1 收到了 我关注的 飞机 的运行状态发生了改变... // 飞机1 现在位置是青海,海拔是300km // 航站楼2 收到了 我关注的 飞机 的运行状态发生了改变... // 飞机1 现在位置是青海,海拔是300km function checkChange2(val, oldVal) { if (val !== oldVal) { plan2.publish(function(){ console.log('飞机2 现在位置是西藏,海拔是500km'); }) } } // 航站楼2 收到了 我关注的 飞机 的运行状态发生了改变... // 飞机2 现在位置是西藏,海拔是500km
添加 观察者 hangzhanlou2
plan2.addSubscriber(hangzhanlou2)
function checkChange1(val, oldVal) { if (val !== oldVal) { plan1.publish(function(){ console.log('飞机1 现在位置是青海,海拔是300km'); }) } } // 航站楼1 收到了 我关注的 飞机 的运行状态发生了改变... // 飞机1 现在位置是青海,海拔是300km // 航站楼2 收到了 我关注的 飞机 的运行状态发生了改变... // 飞机1 现在位置是青海,海拔是300km function checkChange2(val, oldVal) { if (val !== oldVal) { plan2.publish(function(){ console.log('飞机2 现在位置是西藏,海拔是500km'); }) } } // 航站楼1 收到了 我关注的 飞机 的运行状态发生了改变... // 飞机2 现在位置是西藏,海拔是500km // 航站楼2 收到了 我关注的 飞机 的运行状态发生了改变... // 飞机2 现在位置是西藏,海拔是500km
在 飞机1 上移除 观察者 hangzhanlou2
plan1.removeSubscriber(hangzhanlou2)
观察者模式大体就是如此,但是在开发中为了适应各种场景可能会有很多变种,但是万变不离其中。
上面的代码只是用来帮助理解的,针对 航班 与 航站楼 这种情景 还有很多改进的地方。
以上只是个人理解的一些拙见,如有不对之处还请海涵,并希望大家帮忙纠正!
👍
观察者模式
观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。
使用观察者模式的好处:
理解的第一阶段
我们先用一个通俗的情景来比喻一下,就拿 航班 与 航站楼 之间的关系:
图上显示的是 飞机1在从 北京 飞往 南京, 北京航站楼A 和 南京航站楼B 都需要关注 飞机1 的信息, 而 飞机1 在位置变化 之后也要及时的 通知 北京航站楼A 和 南京航站楼。 (其实可以看出来,这里 航站楼是被动的接受 飞机是主动的发出变化)
所以,我们可以写一个简单的 观察/订阅模式
初始化 飞机1 的地理位置
定义 2 个航站楼的行为
南京和北京航站楼开始订阅(注册),将要关注 飞机1 的地址位置
飞机1 经过一段时间的飞行 从 北京 到了 南京 ,这时候地理位置发生了变化
重要 然后 检查(校验) location(地址位置) 是否发生变化,如果发生变化,就进行通知
输出
理解的第二阶段
情况一: 因为这里我们只有一架 飞机1 具有了 观察者模式,如果再来一架 飞机2 也是从 北京 飞往 南京 的呢?很明显,我们也需要知道 飞机2 的地理位置信息
情况二: 还有假如 北京航站楼 只关注 飞机1 的变化,而不关注 飞机2 的变化,同理, 南京航站楼 只关注 _飞机2_的变化, 而不关注 飞机1 的变化
那这样情况就比较复杂了
学术一点的意思就是:要让多个对象都具有观察者模式(让 多个
观察者
可以关注 多个 自己想关注的观察对象
)。这时候分析下,相比较之前有什么变化
用图表示 应该就是下面这种 数据情景
所以这里 我们定义的一个通用函数 ,它应该具备 上述的 一些特性
也就是说:每个观察者对象,都独立维护一套独立的观察者模式。
根据需要,我们抽象出下面 这样一个通用的函数:
OK,已经抽象出了 具体的 范式,下面就结合具体情景来看下看是否符合
下面 先分别定义 飞机1,飞机2 这两个观察者对象的 多个属性(地理位置 和 海拔)
再,分别定义 观察者 的行为(航站楼1,航站楼2 分别关注 飞机的运行状态)
这里,航站楼1 可以 订阅 飞机1,也可以订阅 飞机2 ,也可以都订阅
同样,航站楼1 可以订阅 飞机的 位置,也可以订阅飞机的 海拔, 也可以都订阅
给 飞机1,飞机2的 地理位置,海拔高度 都 添加 观察者模式
添加 观察者 hangzhanlou1
飞机1 经过飞行,判断 关注的信息是否发生了变化
这里判断是伪代码,但是这个判断的逻辑非常!非常!非常!的重要!如果有时间,这个后续会展开讲解,因为这里是另外一个核心内容,diff变化,从而控制View层的渲染!
同样判断状态的改变,进行发布
添加 观察者 hangzhanlou1
同样判断状态的改变,进行发布
添加 观察者 hangzhanlou2
同样判断状态的改变,进行发布
在 飞机1 上移除 观察者 hangzhanlou2
最后
观察者模式大体就是如此,但是在开发中为了适应各种场景可能会有很多变种,但是万变不离其中。
上面的代码只是用来帮助理解的,针对 航班 与 航站楼 这种情景 还有很多改进的地方。
以上只是个人理解的一些拙见,如有不对之处还请海涵,并希望大家帮忙纠正!
拓展阅读