模版方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。在模版方法模式中,子类实现中的逻辑相同部分被上移到父类中,而将不同的部分留待子类来实现。适用场景为2个或多个有相似逻辑的对象
咖啡与茶是一个经典的例子,泡咖啡的步骤如下
var Coffee = function(){};
Coffee.prototype = {
boilWater: function(){
console.log('把水煮沸');
},
brewCoffeeGriends: function(){
console.log('用沸水泡咖啡');
},
pourInCup: function(){
console.log('把咖啡倒进杯子里');
},
addSugarAndMilk: function(){
console.log('加糖和牛奶');
}
}
Coffee.prototype.init = function(){
this.boilWater();
this.brewCoffeeGriends();
this.pourInCup();
this.addSugarAndMilk();
}
var coffee = new Coffee();
coffee.init();
泡一壶茶的步骤如下:
var Tea = function(){};
Tea.prototype = {
boilWater: function(){
console.log('把水煮沸');
},
steepTeaBag: function(){
console.log('用沸水泡茶叶');
},
pourInCup: function(){
console.log('把茶叶倒进杯子里');
},
addLemon: function(){
console.log('加柠檬');
}
}
Tea.prototype.init = function(){
this.boilWater();
this.steepTeaBag();
this.pourInCup();
this.addLemon();
}
var tea = new Tea();
tea.init();
抽离相同逻辑
我们可以发现泡咖啡和泡茶主要由以下不同点
- 原料不同。一个是咖啡,一个是茶,但我们可以把它们都抽象为饮料。
- 泡的方式不同,咖啡是冲泡,茶叶是浸泡,我们可以把它们都抽象为泡。
- 加入的调料不同,一个是糖和牛奶,一个柠檬,但我们可以把他们都抽象为调料。
所以我们抽象出这样一个逻辑:1. 把水煮沸 2. 用沸水冲泡饮料 3. 把饮料倒进杯子 4. 加调料。
不管是冲泡还是浸泡,我们都抽象为一个新的方法,比如说brew
var Beverage = function(){};
Beverage.prototype = {
init: function(){
this.boilWater();
this.brew();
this.pourInCup();
this.addCondiments();
},
//相同的,不需要被重写的方法直接定义实现
boilWater: function(){
console.log('把水煮沸');
},
//空方法,由子类重写,相当于java的抽象类
brew: function(){
},
pourInCup: function(){
},
addCondiments: function(){
}
}
接下来就非常简单了
var Coffee = function(){};
Coffee.prototype = new Beverage();
Coffee.prototype.brew = function(){
//开始具体定义...
}
var coffee = new Coffee();
coffee.init();
//茶叶也一样
在上面的例子中,Beverage.prototype.init方法就是我们的模版方法,该方法封装了子类的算法框架,它作为一个算法的模版,指导子类以何种顺序去执行哪些方法
钩子方法
如果我们上述的步骤有一些额外的操作,想加入一些回调怎么办?钩子(hook)方法可以用来解决问题,放置钩子是隔离变化的一种常见手段,我们在父类中容易变化的地方放置钩子。
Beverage.prototype.init = function(){
this.boilWater();
this.brew();
this.pourInCup();
if(this.customerWantsCondiments()){
this.addCondiments();
}
}
//在子类中
Coffee.prototype.customerWantsCondiments(){
return window.confirm('请问需要调料吗');
}
在实际开发中,有时候需要给一些公用的ajax方法中传入回调,这时候回调也可以写成公用的钩子形式