接着上一篇文章创建模式,本篇讲一下行为模式和结构模式。
行为模式
观察者模式
javascript的事件和事件处理函数,就是观察者模式的表现形式,观察者模式的另外一个名称是发布/订阅者模式
当你创建一个事件处理函数时,比如某一个事件触发时,需要执行一个函数,这就是观察者模式。观察者模式是一种订阅模型,能够让你分配一个对象来监听事件。
下面举一个例子:
Click对象表示一个主题,ClickHandler表示订阅这个主题的观察者。
function Click() {
this.handlers = []; // observers
}
Click.prototype = {
subscribe: function (fn) {
this.handlers.push(fn);
},
unsubscribe: function (fn) {
this.handlers = this.handlers.filter(
function (item) {
if (item !== fn) {
return item;
}
}
);
},
notify: function (o, thisObj) {
var scope = thisObj || window;
this.handlers.forEach(function (item) {
item.call(scope, o);
});
}
}
function run() {
var clickHandler = function (item) {
console.log("notify: " + item);
};
var click = new Click();
click.subscribe(clickHandler);
click.notify('event #1');
click.unsubscribe(clickHandler);
click.notify('event #2');
click.subscribe(clickHandler);
click.notify('event #3');
}
// 打印结果
// notify: event #1
// notify: event #3
结构模式
适配器模式
适配器模式是从一个接口到另一个接口的抽象层或中间体,比较典型的是将API接口返回转换成指定格式的json对象,通过将原始API数据(json、xml等)转换成可用的javascript对象。
下面的例子显示了一个在线购物车,其中一个shipping对象用于计算费用,老的Shipping对象需要被一个新的AdvancedShipping取代,该对象更加安全,并且提供了更好的计算费用的方式。
但是新的AdvancedShipping提供的api和原始shipping对象不一致,这时AdvancedShipping允许将旧的Shipping接口参数提供给新的AdvancedShipping对象,允许了客户端程序能够在没有任何api变动的情况下允许新的AdvancedShipping接口。
// old interface
function Shipping() {
this.request = function (zipStart, zipEnd, weight) {
// ...
return "$49.75";
}
}
// new interface
function AdvancedShipping() {
this.login = function (credentials) { /* ... */ };
this.setStart = function (start) { /* ... */ };
this.setDestination = function (destination) { /* ... */ };
this.calculate = function (weight) { return "$39.50"; };
}
// adapter interface
function ShippingAdapter(credentials) {
var shipping = new AdvancedShipping();
shipping.login(credentials);
return {
request: function (zipStart, zipEnd, weight) {
shipping.setStart(zipStart);
shipping.setDestination(zipEnd);
return shipping.calculate(weight);
}
};
}
function run() {
var shipping = new Shipping();
var credentials = { token: "30a8-6ee1" };
var adapter = new ShippingAdapter(credentials);
// original shipping object and interface
var cost = shipping.request("78701", "10010", "2 lbs");
console.log("Old cost: " + cost);
// new shipping object with adapted interface
cost = adapter.request("78701", "10010", "2 lbs");
console.log("New cost: " + cost);
}