对js中观察者模式的一点理解

在观察者模式中, 当被观察者做出某动作时, 会通知关注它的观察者, 观察者收到通知后会做出相应的动作. 以歌星和粉丝为例, 歌星具有唱歌发布唱片的职能, 当歌星发布唱片时会通知关注他的粉丝们,粉丝们收到通知以后会做出相应的反应(购买唱片或是不买, 还上网收听, 由粉丝自己决定), 在这个过程中, 歌星只具有唱歌并通知他的粉丝的的职能, 而粉丝们则只能收到消息后做出自己的反应(购买或是不买, 自己决定), 这就是所谓的单一职责原则,由于明星和粉丝之间的联系只有通知,没有其他的关联, 大大降低了两者之间的耦合, 这就是解耦

首先定义一个明星类, 并创建一个集合来存储关注他的粉丝

1
2
3
var Star = function() {                // 明星
this.fansList = []; // 明星的粉丝
}

该明星具有唱歌的职能, 唱歌时需要通知关注他的粉丝们(会调用粉丝们的action方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Star.prototype = {
constructor: Star,
song: function(song) {
this.notify(song);
},
notify:function(song) { // 明星有活动时通知粉丝

console.log('明星发布了唱片' + song);
this.fansList.forEach(function(fans, i) {
console.log('明星向粉丝发布发布唱片<<' + song + '>> 的消息');
fans.action.call(this, song);
});
}
}

创建粉丝类

1
2
3
var Fans = function() {     // 粉丝

}

粉丝具有关注哪个明星的功能(把自己加入所关注明生的粉丝列表中), 也有收到明星消息后作出反应的功能

1
2
3
4
5
6
7
8
9
10
Fans.prototype = {
constructor: Fans,
action:function(song) { // 粉丝的行动
// do something

},
follow: function(star) { // 关注哪个明星
star.fansList.push(this);
}
}

创建一个明星s

1
var s = new Star();

创建一个粉丝f1并关注明星s, 同时定义该粉丝收到消息后会做出什么样的动作

1
2
3
4
5
var f1 = new Fans();
f1.follow(s);
f1.action = function(song) { // 至于收到消息后做什么,由粉丝自己决定 
console.log('我是铁杆粉丝f1,我去购买唱片<<' + song + '>>');
}

再创建一个粉丝f2

1
2
3
4
5
var f2 = new Fans();
f2.follow(s);
f2.action = function(song) {
console.log('我是铁杆粉丝f2,但买不起唱片,只能去借别人的唱片<<' + song + '>>');
}

明星发布唱片(看来此明星是华仔)

1
s.song('一起走过的日子');

而在js中最经典的应用就是对事件的处理, 包括浏览器原生事件和自定义事件

以jq为便, 页面中有一个id为btn 的button, 通过jq为该按钮添加监听事件

1
2
3
$('#btn').on('click', function(e) {
alert('我被点击了!')
});

然后通过程序触发

1
$('#btn').trigger('click');

可能有人会有疑问, 在这里, 到底谁是观察者, 谁是被观察者呢?

因为在多数教程中, 观察者和被观察者往往是被设计的很分明的, 比如定义一个被观察者的类或对象, 具有发布消息的功能, 再定义一个观察者的类或对象, 具有接受被观察者通知并做出反应的功能, 在这里,两者的定义是非常明确的, 很好理解,但是在上面的这个jq的例子中, 到底谁是观察者, 谁又是被观察者呢?

我们先来看下观察者, 观察者, 首先要有订阅的功能, 反映在这里就是监听click事件什么时候被触发, 那在这里, 谁在监听呢, jq中的on是添加监听的意思, 所以执行on方法的对象就是观察者, 即btn按钮.

谁又是被观察者呢? 我们再来看下被观察者的功能,被观察者具有发布通知的功能,反映在这里就是触发click事件,而触发click事件的方法的执行者,又是$(‘#btn’)! 所以,在这里,观察者和被观察者都是按钮本身,按钮扮演着观察者和被观察者两个角色,监听事件的时候,按钮是观察者,触发事件的时候,按钮是被观察者,这正是事件处理中观察者模式与其他地方不一样的地方.

但是,为什么要这么做呢?为什么要让一个按钮扮演观察者和被观察者两个不同的角色呢? 因为, 我们最终关心的是谁被注册了什么事件, 以及事件被触发时做了什么动作, 而不关心监听和触发的对象是不是不同的, 所以, 在观察者和被观察者的设计上我们有了更灵活的空间, 我们可以设计将观察者和被观察者设计成不同的对象,也可以设计成一个对象, 很明显, 在这里我们没有必要将他们设计成两个不同的对象