模板方法是一種只需要使用繼承就可以實現(xiàn)的非常簡單的模式
模板方法由兩部分組成,一是抽象的父類,二是具體實現(xiàn)的子類。通常在抽象父類中封裝了子類的算法框架,包括實現(xiàn)一些公共方法以及封裝子類中所有方法的執(zhí)行順序。
參考《javascript模式設(shè)計與開發(fā)實踐》第11章
這個模式方法很有意思,在使用之前需要有一個抽象的額過程,是對類似的生產(chǎn)過程的具體細節(jié)的理解,找出流程中的共同點作為抽象的父類。
咖啡和茶的沖泡過程
泡咖啡
|
泡茶
|
|---|---|
| 把水煮沸 | 把水煮沸 |
用沸水泡咖啡
|
用沸水浸泡茶葉
|
把咖啡倒進杯子 |
把茶水倒進杯子 |
加糖和牛奶
|
加檸檬
|
把泡咖啡和泡茶的不同點標(biāo)示出來,剩下的部分就是共同的部分
- [ ] 原料不同,一個是咖啡,一個是茶。抽象為飲料
- [ ] 泡的方式不同,一個是泡,一個是浸泡。統(tǒng)一抽象為泡
- [ ] 加入的調(diào)味品不同,一個是糖和牛奶,一個是檸檬。抽象為調(diào)料
整個沖泡飲料的過程就抽象為下面的流程
- 把水煮沸
- 用沸水沖泡飲料
- 把飲料倒進被子
- 加調(diào)料
抽象的父類的代碼
var Beverage = function(){};
Beverage.prototype.boilWater = function(){
console.log( '把水煮沸' );
};
Beverage.prototype.brew = function(){}; // 空方法,應(yīng)該由子類重寫
Beverage.prototype.pourInCup = function(){}; // 空方法,應(yīng)該由子類重寫
Beverage.prototype.addCondiments = function(){}; // 空方法,應(yīng)該由子類重寫
Beverage.prototype.init = function(){//定義子類的函數(shù)執(zhí)行順序
this.boilWater();
this.brew();
this.pourInCup();
this.addCondiments();
};
有了父類的模板,在煮茶是就依照父類的模板來使用,通用部分不動,特殊的地方重寫父類的方法
茶葉子類繼承父類的模板
var Tea = function(){}; //定義煮茶子類
Tea.prototype = new Beverage(); //繼承父類
Tea.prototype.brew = function(){ //重寫父類方法
console.log( '用沸水浸泡茶葉' );
};
Tea.prototype.pourInCup = function(){ 重寫父類方法
console.log( '把茶倒進杯子' );
};
Tea.prototype.addCondiments = function(){ 重寫父類方法
console.log( '加檸檬' );
};
var tea = new Tea(); //實例化子類
tea.init(); //依照父類的方法執(zhí)行順序來執(zhí)行
//由于javascript的對象原型繼承鏈的方式和java的繼承是完全不同的,這里代碼結(jié)構(gòu)和java類似,但是里面的機制是不同的
```
鉤子方法
- 把水煮沸
- 用沸水沖泡飲料
- 把飲料倒進被子
- 加調(diào)料
上面是一般的沖調(diào)飲料的流程,但是可能會有特殊情況,比如有的人可能不會加調(diào)料。遇到這種情況需要使用鉤子方法來在隔離有變化的步驟
var Beverage = function(){};
Beverage.prototype.boilWater = function(){
console.log( '把水煮沸' );
};
Beverage.prototype.brew = function(){
throw new Error( '子類必須重寫brew 方法' );
};
Beverage.prototype.pourInCup = function(){
throw new Error( '子類必須重寫pourInCup 方法' );
};
Beverage.prototype.addCondiments = function(){
throw new Error( '子類必須重寫addCondiments 方法' );
};
Beverage.prototype.customerWantsCondiments = function(){
return true; // 默認需要調(diào)料
};
Beverage.prototype.init = function(){
this.boilWater();
this.brew();
this.pourInCup();
if ( this.customerWantsCondiments() ){ // 如果掛鉤返回true,則需要調(diào)料
this.addCondiments(); //第四步就變?yōu)榭蛇x的方法了
}
};
實例化沖泡咖啡的實例
var CoffeeWithHook = function(){};
CoffeeWithHook.prototype = new Beverage();
CoffeeWithHook.prototype.brew = function(){
console.log( '用沸水沖泡咖啡' );
};
CoffeeWithHook.prototype.pourInCup = function(){
console.log( '把咖啡倒進杯子' );
};
CoffeeWithHook.prototype.addCondiments = function(){
console.log( '加糖和牛奶' );
};
CoffeeWithHook.prototype.customerWantsCondiments = function(){
return window.confirm( '請問需要調(diào)料嗎?' );//對顧客選擇的判斷條件
};
var coffeeWithHook = new CoffeeWithHook();
模板方法通過封裝變化提高系統(tǒng)擴展能力。