装饰模式:在不改变原类(对象)和继承的情况下动态扩展对象功能,通过包装一个对象来实现一个新的具有原对象相同接口的新的对象。
在设计原则中,有一条,多用组合,少用继承,装饰模式正是这一原则的体现。
UML示意图:
假设自行车商店有4种自行车卖:
var ABicycle = function(){ ... }; var BBicycle = function(){ ... }; var CBicycle = function(){ ... }; var DBicycle = function(){ ... };
自行车商店需要给自行车加些配件,比如车筐,尾灯等,如果给每一类自行车创建一个子类:
var ABicycleWithBell = function(){ ... }; var BBicycleWithBasket = function(){ ... }; var CBicycleWithLight = function(){ ... };
带有铃铛的A类车,带有车筐的B类车,带有尾灯的C类车,导致的结果,就是N种配置会有4N个子类,当然不是一个可取的方法。
可以将铃铛等东西设置为实例属性,但是对于方法的扩展,比如变速等,就可以使用装饰模式。
A类自行车:
function ABicycle(){ } ABicycle.prototype = { wash : function(){ }, ride : function(){ }, getPrice : function(){ return 888; } }
铃铛:
function bicycleBell( bicycle ){ var price= bicycle.getPrice(); bicycle.bell = function(){ console.log("ding! ding!"); }; bicycle.getPrice = function(){ return price + 100; }; return bicycle; }
加铃铛:
var bicycleA = new ABicycle(); bicycleA = bicycleBell( bicycleA );
如果是:
bicycle.getPrice = function(){ return bicycle.getPrice() + 100; };
会导致无限循环调用,换个方法:
function BicycleBell( bicycle ){ this.bicycle = bicycle; } BicycleBell.prototype = { wash : function(){ return this.bicycle.wash(); }, ride : function(){ return this.bicycle.ride(); }, getPrice : function(){ return this.bicycle.getPrice() + 100; }, bell : function(){ console.log("ding! ding!"); } }
包装对象,再次模拟原对象,这种方式较好的解决了对于某些需要修改并且依赖原始该方法的方法生成形式。但是有点繁琐,可以提取一下,通用继承函数:
function extend( subClass, superClass ){ var F = function(){}; F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass; subClass.superclass = superClass.prototype; if( superClass.prototype.constructor === Object.prototype.constructor ){ superClass.prototype.constructor = superClass; } }
装饰角色:
function BicycleDecorator( bicycle ){ this.bicycle = bicycle; } BicycleDecorator.prototype = { wash : function(){ return this.bicycle.wash(); }, ride : function(){ return this.bicycle.ride(); }, getPrice : function(){ return this.bicycle.ride(); } }
使用extend:
var BicycleBell = function( bicycle ){ BicycleBell.superclass.constructor.call( this, bicycle ); } extend( BicycleBell, BicycleDecorator ); BicycleBell.prototype.bell = function(){ console.log("ding! ding!"); } BicycleBell.prototype.getPrice = function(){ return this.bicycle.getPrice() + 100; }
装铃铛:
var bicycleA = new ABicycle(); bicycleA = new BicycleBell( bicycleA );
装饰者模式是为已有功能动态地添加更多功能的一种方式,把每个要装饰的功能放在单独的函数里,然后用该函数包装所要装饰的已有函数对象,因此,当需要执行特殊行为的时候,调用代码就可以根据需要有选择地、按顺序地使用装饰功能来包装对象。优点是把类(构造函数)的核心职责和装饰功能区分开了。