发布(Publish)/订阅(Subscribe)模式是一种和观察者(Observer)模式很相似的设计模式,以至于很多人把这两种模式看作是同一种模式。
在观察者模式中的Subject就像一个发布者(Publisher),而观察者(Observer)完全可以看作一个订阅者(Subscriber)。subject通知观察者时,就像一个发布者通知他的订阅者。
1.观察者模式和发布/订阅模式的概念
-
观察者模式
目标和观察者是基类,目标提供维护观察者的一系列方法,观察者提供更新接口。具体观察者和具体目标继承各自的基类,然后具体观察者把自己注册到具体目标里,在具体目标发生变化时候,调度观察者的更新方法。
-
发布/订阅模式
订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度订阅者注册到调度中心的处理代码。
我们再用一个图来直观地感受一下两种模式之间的区别:
观察者模式和发布/订阅模式的区别是:
- 从上面的图片中可以看到,最大的区别是调度的地方。虽然两种模式都存在订阅者和发布者(具体观察者可认为是订阅者、具体目标可认为是发布者),但是观察者模式是由具体目标调度的,而发布/订阅模式是统一由调度中心调的,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会。
- 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
2.观察者模式实现
下面这段代码摘抄自JavaScript 设计模式一书
-
2.1对观察者和被观察者进行建模:
- 2.2 扩展函数
function extend(extension,obj){
for(var key in extension){
obj[key]=extension[key]; }
}
- 2.3使用范例
html:
<button id="addNewObserver">Add New Observer checkbox</button>
<input id="mainCheckbox" type="checkbox"/>
<div id="observersContainer"></div>
script:
//注意要把2-1和2-2封装好的函数copy进来
//我们DOM元素的引用
var controlCheckbox=document.getElementById("mainCheckbox"),
addBtn=document.getElementById("addNewObserver"),
container=document.getElementById("observersContainer");
//具体的被观察者 //Subject类扩展controlCheckbox类
extend(new Subject(),controlCheckbox);
//点击checkbox将会触发对观察者的通知
controlCheckbox["onclick"]=new Function("controlCheckbox.notify(controlCheckbox.checked)");
addBtn["onclick"]=AddNewObserver;
//具体的观察者
function AddNewObserver(){
//建立一个新的用于增加的checkbox
var check =document.createElement("input"); check.type="checkbox";
//使用Observer类扩展
extend(new Observer(),check);
//使用定制的Update函数重载
check.update=function(value){
this.checked=value;
};
//增加新的观察者到我们主要的被观察者的观察者列表中
controlCheckbox.addObserver(check);
//将元素添加到容器的最后
container.appendChild(check);
}
运行结果截图:
3.发布/订阅模式实现
发布/订阅在JavaScript的生态系统中非常合适,主要是因为作为核心的ECMAScript 实现是事件驱动的。尤其是在浏览器环境下更是如此,因为DOM 使用事件作为其主要的用于脚本的交互API。
- 测试
var messageLogger=function(topics,data){
console.log("Logging:"+topics+":"+data);
};
var subscription=pubsub.subscribe("inbox/newMessage",messageLogger);
pubsub.publish("inbox/newMessage","helloworld!");
// or
pubsub.publish("inbox/newMessage",["test","a","b","c"]);
// or
pubsub.publish("inbox/newMessage",{
sender:"hello@google.com",
body:"Heyagain!"
});
-
运行结果
关于发布/订阅模式的渐进实现大家可以参考JS设计模式之订阅模式
参考文章:
观察者模式与发布/订阅模式区别
【Javascript设计模式3】-观察者模式
《JavaScript 设计模式》一书