模板方法模式是一種只需使用繼承就可以實現的非常簡單的模式。模板方法模式由兩部分結構組成,第一部分是抽象父類,第二部分是具體的實現子類。通常在抽象父類中封裝了子類的演算法框架,包括實現一些公共方法以及封裝子類中所有方法的執行順序。子類通過繼承這個抽象類,也繼承了整個演算法結構,並且可以選擇重寫父類的方法。
故事背景
我們現在需要衝泡一杯咖啡和一壺茶,它們的操作步驟基本上是一樣的,如下所示
泡咖啡 | 泡茶 |
---|---|
把水煮沸 | 把水煮沸 |
用沸水沖泡咖啡 | 用沸水浸泡茶葉 |
把咖啡倒進杯子 | 把茶水倒進杯子 |
加糖和牛奶 | 加檸檬 |
程式碼實現(繼承)
/*建立一個抽象父類來表示泡一杯飲料的整個過程。不論是 Coffee,還是 Tea,都被我們用Beverage來表示*/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.init = function(){
this.boilWater();
this.brew();
this.pourInCup();
this.addCondiments();
};
/*建立Coffee類*/var Coffee = function(){
};
Coffee.prototype = new Beverage();
Coffee.prototype.brew = function(){
console.log( '用沸水沖泡咖啡' );
};
Coffee.prototype.pourInCup = function(){
console.log( '把咖啡倒進杯子' );
};
Coffee.prototype.addCondiments = function(){
console.log( '加糖和牛奶' );
};
var Coffee = new Coffee();
Coffee.init();
/*建立Tea 類*/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();
複製程式碼
思考:真的需要繼承嗎?
模板方法模式中,父類封裝了子類的演算法框架和方法的執行順序,子類繼承父類之後,父類通知子類執行這些方法,好萊塢原則 很好地詮釋了這種設計技巧
好萊塢原則
好萊塢無疑是演員的天堂,但好萊塢也有很多找不到工作的新人演員,許多新人演員在好萊 塢把簡歷遞給演藝公司之後就只有回家等待電話。有時候該演員等得不耐煩了,給演藝公司打電 話詢問情況,演藝公司往往這樣回答:“不要來找我,我會給你打電話。”在設計中,這樣的規則就稱為好萊塢原則。在這一原則的指導下,我們允許底層元件將自己 掛鉤到高層元件中,而高層元件會決定什麼時候、以何種方式去使用這些底層元件,高層元件對 待底層元件的方式,跟演藝公司對待新人演員一樣,都是“別呼叫我們,我們會呼叫你”。
程式碼重構(好萊塢原則)
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;
};
var Coffee = Beverage({
brew: function(){
console.log( '用沸水沖泡咖啡' );
}, pourInCup: function(){
console.log( '把咖啡倒進杯子' );
}, addCondiments: function(){
console.log( '加糖和牛奶' );
}
});
var Tea = Beverage({
brew: function(){
console.log( '用沸水浸泡茶葉' );
}, pourInCup: function(){
console.log( '把茶倒進杯子' );
}, addCondiments: function(){
console.log( '加檸檬' );
}
});
var coffee = new Coffee();
coffee.init();
var tea = new Tea();
tea.init();
複製程式碼
總結
模板方法模式是一種典型的通過封裝變化提高系統擴充套件性的設計模式。在傳統的物件導向語 言中,一個運用了模板方法模式的程式中,子類的方法種類和執行順序都是不變的,所以我們把這部分邏輯抽象到父類的模板方法裡面。而子類的方法具體怎麼實現則是可變的,於是我們把這 部分變化的邏輯封裝到子類中。通過增加新的子類,我們便能給系統增加新的功能,並不需要改動抽象父類以及其他子類,這也是符合開放封閉原則的。