Javascript Patterns--读书笔记10 (Decorator)

Decorator模式是指可以在运行的时候让一些功能附加到对象上,当在静态类语言中实现的时候,也许不那么容易,但是在JS中,我们借助于它自己的语言特性,可以很容易的让其实现这种模式
应用场景
让我们来看一个这个模式应用的例子,假若你现在正在做一个卖东西的web应用。假设每一件新的商品,我们都抽像为一sale object,我们可以通过sale.getPrice()来得到商品的价格。假定这样一个场景,一个顾客在加拿大的Qubec省买了这样一件产品,那么他需要交纳联邦政府的税,还需要交纳Qubec省的税。我们可以这样来应用decorate模式,用联邦政府税decorator和Qubec省税deocraor来decorate它。我们来看下面的示例代码.

var sale = new Sale(100);//商品的价格是100
sale = sale.decorate('fedtax');//联邦征税
sale = sale.decorate('quebec');//省征税
sale = sale.decorate('money');//加上税以后的价格
sale.getPrice();//112.88

而在另一个场景中,那个购买者可能是另外一个省的,而这个省对这件商品是不征税的,而不过它在结算的时候,用的是加拿大元,而不再是美元,那让我们来看下面的代码:

View Code
var sale = new Sale(100);
sale = sale.decorate('fedtax');
sale = sale.decorate('cdn');//改一下货币单位
sale.getPrice();//"CDN$ 105.00"

我们可以灵活的在运行的时候,来对这个对象在不同的场景下来附加上不同的功能,从而得到不同价格,那我们来看一下,如何实现这种模式.

实现
一种实现的方式是使每个decorator是一个object,每个decorator都含有一些可以被复写的方法。每一个decorator实际上继承了上一次被前一个decorator增加的方法。每一个要执行decorate的方法都会调用它父类的相同名称的方法,然后它父对象的方法又会执行和它一样的方法。
最后的效果是当你执行sale.getPrice()的时候,你会先调用money decorator,即sale.decorate('money')或者是sale.decorate('cdn'),然后money deocrator又会调用它父对象的相同名称的方法即quebec.getPrice()最后到Sale()构造函数实现的那个getPrice(),请看下图

 

 

我们首先实现一个构造函数,然后添加到prototype中一个方法:

View Code
funciton Sale(price) {
    this.price = price || 100;
}

Sale.prototype.getPrice = function() {
    return this.price;
}

decocrators都被实现成构造函数的属性

Sale.decorators = {};

来看deocrator的具体实现,它们实现了自己的getPrice().请注意这些getPrice()首先会去调用parent object的getPrice()

View Code
Sale.decorators.fedtax = {
    getPrice : function() {
        var price = this.uber.getPrice();
        price += price * 5 /100;
        return price;
    }
}
Sale.decorators.quebec = {
    getPrice: function() {
        var price = this.uber.getPrice();
        price += price *7.5 /100;
        return price;
    }
}
Sale.decorators.money = {
    getPrice: function() {
        return "$" + this.uber.getPrice().toFixed(2);
    }
}
Sale.decorators.cdn = {
    getPrice: function() {
        return "CDN$" + this.uber.getPrice().toFixed(2);
    }
}

最后让我们来看一下deocrate是如何实现的,首先让我们来看一下,如何调用decorate, sale = sale.decorate('fedtax');'fedtax'它对应着一个object, Sale.decorators.fedtax.新的deocrate以后的newobj将会继承现在已有的对象,要么是最初的对象,要么是已经被装饰过的。为了实现继承部分,我们用到一个暂时的空constructor,我们还设置uber,让它的children可以访问parent.

View Code
Sale.prototype.decorate = function(decorator) {
    var F = function () {};
        overrides = this.constructor.decorators[decorator],//请注意这里的构造函数没有被改过,始终是Sale()
        i, newobj;
    F.protoype = this;//要么是原来的new Sale(),要么是被装饰过的object
    newobj = new F();
    newobj.uber = F.prototype;
    for( i in overrides) {
        if( overrides.hasOwnProperty(i)) {
            newobj[i] = overrides[i];
        }
    }
    return newobj;
}


用List()来实现
我们把所有的deocrator对象全部加到一个list中,然后当我们需要计算价格的时候,我们只需要遍历整个list,让所有的decorator对象执行所有的decorate方法即可:

View Code
function Sale(price) {
    this.price = (price >0) || 100;
    this.decorators_list = [];
}

Sale.decorators = {};

Sale.decorators.fedtax = {
    getPrice: function(price) {
        return price + price * 5 / 100;
    }
};

Sale.decorators.quebec = {
    getPrice: function(price) {
        return price + price * 7.5 / 100;
    }
};

Sale.decorators.money = {
    getPrice: function(price) {
        return "$" + price.toFixed(2);
    }
};

Sale.prototype.deocrate = function (decorator) {
    this.decorators_list.push(decorator);
};

Sale.prototype.getPrice = function () {
    var price = this.price,
        i,
        max = this.decorators_list.length,
        name;
    for(i =0; i < max; i+=1) {
        name = this.decorators_list[i];
        price = Sale.decorators[name].getPrice(price);
    }
    return price;
};
posted @ 2012-10-11 22:19  moonreplace  阅读(259)  评论(0编辑  收藏  举报