发布订阅

是什么

其实24种基本的设计模式中并没有发布订阅模式,他只是观察者模式的一个别称。

但是经过时间的沉淀,似乎他已经强大了起来,已经独立于观察者模式,成为另外一种不同的设计模式。

在现在的发布订阅模式中,称为发布者的消息发送者不会将消息直接发送给订阅者,这意味着发布者和订阅者不知道彼此的存在。在发布者和订阅者之间存在第三个组件,称为调度中心或事件通道,它维持着发布者和订阅者之间的联系,过滤所有发布者传入的消息并相应地分发它们给订阅者。

举一个例子,你在微博上关注了A,同时其他很多人也关注了A,那么当A发布动态的时候,微博就会为你们推送这条动态。A就是发布者,你是订阅者,微博就是调度中心,你和A是没有直接的消息往来的,全是通过微博来协调的(你的关注,A的发布动态)。

与观察者模式的区别

发布订阅模式相比观察者模式多了个事件通道,事件通道作为调度中心,管理事件的订阅和发布工作,彻底隔绝了订阅者和发布者的依赖关系。即订阅者在订阅事件的时候,只关注事件本身,而不关心谁会发布这个事件;发布者在发布事件的时候,只关注事件本身,而不关心谁订阅了这个事件。

实现

简单实现的发布订阅:

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>发布订阅</title>
</head>
<body>
<script>

var pubsub = {};

(function(myObject) {

// Storage for topics that can be broadcast 可以广播的主题的存储
// or listened to 或者听
var topics = {};

// A topic identifier 主题标识符
var subUid = -1;

// Publish or broadcast events of interest 发布或广播感兴趣的事件
// with a specific topic name and arguments 具有特定的主题名称和参数
// such as the data to pass along 如数据传递
myObject.publish = function(topic, args) {

if (!topics[topic]) {
return false;
}

var subscribers = topics[topic];
var len = subscribers ? subscribers.length : 0;

while (len--) {
subscribers[len].func(topic, args);
}

return this;
};

// Subscribe to events of interest 订阅感兴趣的事件
// with a specific topic name and a 具有特定的主题名称和
// callback function, to be executed 回调函数,将被执行
// when the topic/event is observed 当观察到主题/事件时
myObject.subscribe = function(topic, func) {

if (!topics[topic]) {
topics[topic] = [];
}

var token = (++subUid).toString();
topics[topic].push({
token: token,
func: func
});
return token;
};

// Unsubscribe from a specific 取消订阅
// topic, based on a tokenized reference 主题,基于标记化参考
// to the subscription 订阅
myObject.unsubscribe = function( token ) {
for (var m in topics) {
if (topics[m]) {
for ( var i = 0, j = topics[m].length; i < j; i++ ) {
if ( topics[m][i].token === token ) {
topics[m].splice( i, 1 );
return token;
}
}
}
}
return this;
};
}(pubsub));

// A simple message logger that logs any topics and data received through our 一个简单的消息记录器,记录通过我们接收的任何主题和数据。
// subscriber 用户
var messageLogger = function (topics, data) {
console.log("Logging:" + topics + ": " + data);
};


// Subscribers listen for topics they have subscribed to and 订阅者监听他们订阅的主题和
// invoke a callback function (e.g messageLogger) once a new 调用一个新的回调函数(例如MasigelgGER)
// notification is broadcast on that topic 这个主题广播通知
var subscription = pubsub.subscribe("inbox/newMessage", messageLogger);

// Publishers are in charge of publishing topics or notifications of 出版商负责出版主题或通知。
// interest to the application. e.g: 对应用程序有兴趣。例如:

pubsub.publish("inbox/newMessage", "hello world!");

// or
pubsub.publish("inbox/newMessage", ["test", "a", "b", "c"]);

// or
pubsub.publish("inbox/newMessage", {
sender: "hello@google.com",
body: "Hey again!"
});

// We can also unsubscribe if we no longer wish for our subscribers 如果我们不再希望我们的订户,我们也可以退订。
// to be notified 被通知
pubsub.unsubscribe(subscription);

// Once unsubscribed, this for example won't result in our 一旦退订,这将不会导致我们的
// messageLogger being executed as the subscriber is MasaGelgGER作为订阅服务器执行
// no longer listening 不再听
pubsub.publish("inbox/newMessage", "Hello! are you still there?");
</script>
</body>
</html>