什么是装饰者模式
装饰者模式可以动态地给一个对象添加一些额外的职责。就增加功能来说,装饰者模式比通过继承生成子类更为灵活。
下面通过一个例子来详细的介绍一下。
我们销售一台电脑,每个电脑都是一个新的Computer对象,它都有一个price属性来表示价格,并且可以通过它的getPrice方法来得到它的价格:
var Computer = function(price){ this.price = price || 100; }; Computer.prototype.getPrice = function(){ return this.price; };
现在有个客户想要再加个内存条,如果通过继承的方式,我们要新增一个ComputerWithMemory对象。
var ComputerWithMemory = function(price){ Computer.constructor.call(this, price); }; ComputerWithMemory.prototype = new Computer(); ComputerWithMemory.prototype.getPrice = function(){ return this.price + 20; };
我们来验证一下:
var computer = new Computer(); var computer1 = new ComputerWithMemory(); computer.getPrice(); // 100 computer1.getPrice(); // 120
如果客户要加个固态硬盘,我们就有要再新增一个ComputerWithSSD对象,如果客户既要内存条,又要固态硬盘,那我们就要再新增一个ComputerWithMemoryAndSSD对象。
下面我们来用装饰者模式实现:
我们在Computer中加一个_decorateList属性来保存添加到对象上的装饰者列表:
var Computer = function(price){ this.price = price || 100; this._decoratorList = []; };
接下来,我们在Computer对象上预先定义好需要的装饰者:
Computer.decorators = {}; Computer.decorators.memory = { getPrice : function(price){ return price + 20; } };
Computer.decorators用来存储我们的装饰者。上面我们定义了一个加内存条的装饰者memory,注意它的getPrice方法是带参数price的。
到这里装饰者我们已经有了,但是装饰者是需要注册到具体对象上去的,我们就需要在加一个注册的方法:
Computer.prototype.decorate = function(decorate){ this._decoratorList.push(decorate); };
注册方法很简单,就是把要注册的装饰者加入到对象的装饰者列表中。参数decorate表示装饰者的名字,例如如果是上面的memory装饰者,参数decorate就是字符串“memory”。
最后我们再重写一下getPrice方法:
Computer.prototype.getPrice = function(){ // 先得到我们的原价 var price = this.price; // 遍历注册了的装饰者 for(var i = 0, len = this._decoratorList.length; i < len; i++){ var name = this._decoratorList[i]; // 根据装饰者的名字得到装饰者 // 再调用装饰者的getPrice方法来处理我们的价格price price = Computer.decorators[name].getPrice(price); } return price; };
这样我们的装饰者模式就完成了,我们再来测试一下:
var computer = new Computer(); computer.getPrice(); // 100 // 加个内存条 computer.decorate('memory'); computer.getPrice(); // 120
现在如果我们要想加一个固态硬盘,我们只要在Computer对象上再加个ssd装饰者:
Computer.decorators.ssd = { getPrice : function(price){ return price + 30; } };
再来测试一下:
var computer = new Computer(); // 加个固态硬盘 computer.decorate('ssd'); computer.getPrice(); // 130 // 再加个内存条 computer.decorate('memory'); computer.getPrice(); // 150