由两部分组成:第一部分是抽象父类;第二部分是具体的实现子类。
金典例子:Coffee or Tea
// 1. 创建父类,添加原型公用方法
var Beverage = function(){};
Beverage.prototype.boilWater = function(){
console.log( '把水煮沸' );
};
Beverage.prototype.brew = function(){}; // 空方法,应该由子类重写
Beverage.prototype.pourInCup = function(){}; // 空方法,应该由子类重写
Beverage.prototype.addCondiments = function(){}; // 空方法,应该由子类重写
Beverage.prototype.init = function(){
this.boilWater();
this.brew();
this.pourInCup();
this.addCondiments();
};
// 2.创建子类,并原型继承父类,重写父类的抽象方法
// 2-1 创建子类
var Coffee = function(){};
Coffee.prototype = new Beverage();
// 2-2 根据需求,重写父类方法
Coffee.prototype.brew = function(){
console.log( '用沸水冲泡咖啡' );
};
Coffee.prototype.pourInCup = function(){
console.log( '把咖啡倒进杯子' );
};
Coffee.prototype.addCondiments = function(){
console.log( '加糖和牛奶' );
};
// 3 子类实例化,调用执行
var Coffee = new Coffee();
Coffee.init();
上面代码中Beverage.prototype.init
就是模板方法,因为它封装了子类的算法框架,也规定了它们的执行顺序。
现在,有另外一种场景:父类封装的算法框架适用于大多数子类,但有些“个性化”的子类,不想执行其中的某个步骤,怎么办?
钩子方法:
// 原来的模板方法
Beverage.prototype.init = function(){
this.boilWater();
this.brew();
this.pourInCup();
this.addCondiments();
};
// 改进方案:
// 1 添加钩子方法(hook),决定执行步骤
Beverage.prototype.init = function(){
this.boilWater();
this.brew();
this.pourInCup();
if ( this.customerWantsCondiments() ){ // 如果挂钩返回true,则需要调料
this.addCondiments();
}
}
// 2 子类重写钩子方法
CoffeeWithHook.prototype.customerWantsCondiments = function(){
return window.confirm( '请问需要调料吗?' );
};
如果不用继承,怎么实现模板方法模式?
为了方便描述,还是使用父类和子类概念。
- 父类封装算法框架,并接受参数,更改重写方法。
- 子类需要实例化,所以父类要返回一个构造函数。
- 如果仅仅是事项功能,不需要第2条,返回一个init()就行。
// 1. 抽象函数
var Beverage = function( param ){
var boilWater = function(){
console.log( '把水煮沸' );
};
var brew = param.brew || function(){
throw new Error( '必须传递brew 方法' );
};
var pourInCup = param.pourInCup || function(){
throw new Error( '必须传递pourInCup 方法' );
};
var addCondiments = param.addCondiments || function(){
throw new Error( '必须传递addCondiments 方法' );
};
var F = function(){};
F.prototype.init = function(){
boilWater();
brew();
pourInCup();
addCondiments();
};
return F;
};
// 2. 返回具体的子类
var Coffee = Beverage({
brew: function(){
console.log( '用沸水冲泡咖啡' );
},
pourInCup: function(){
console.log( '把咖啡倒进杯子' );
},
addCondiments: function(){
console.log( '加糖和牛奶' );
}
});
// 3. 子类实例化,调用
var coffee = new Coffee();
coffee.init();
直接返回init
// 1. 抽象函数
var Beverage = function( param ){
var boilWater = function(){
console.log( '把水煮沸' );
};
var brew = param.brew || function(){
throw new Error( '必须传递brew 方法' );
};
var pourInCup = param.pourInCup || function(){
throw new Error( '必须传递pourInCup 方法' );
};
var addCondiments = param.addCondiments || function(){
throw new Error( '必须传递addCondiments 方法' );
};
// 仅仅实现功能,直接返回init
var init = function(){
boilWater();
brew();
pourInCup();
addCondiments();
};
return init;
};
// 2. 返回具体的子类
var Coffee = Beverage({
brew: function(){
console.log( '用沸水冲泡咖啡' );
},
pourInCup: function(){
console.log( '把咖啡倒进杯子' );
},
addCondiments: function(){
console.log( '加糖和牛奶1231313' );
}
});
// 3.直接调用
Coffee()
网友评论