javascript设计模式(二)

356 阅读2分钟

接着上一篇文章创建模式,本篇讲一下行为模式结构模式

行为模式

观察者模式

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);
}