面試官:說說你對模板方法模式的理解?應(yīng)用場景?
一、是什么
在模板模式(Template Pattern)中,一個抽象類公開定義了執(zhí)行它的方法的方式/模板,它的子類可以按需要重寫方法實現(xiàn),但調(diào)用將以抽象類中定義的方式進行
這種類型的設(shè)計模式屬于行為型模式
由兩部分組成:
抽象父類:封裝子類的算法框架,包括實現(xiàn)一些公用方法以及封裝在子類中所有方法的執(zhí)行順序
具體的實現(xiàn)子類:通過繼承這個抽象類,也繼承了整個算法,并且可以選擇重寫父類的方法
二、使用
例如現(xiàn)在需要泡一杯茶和咖啡,過程如下:
不管是泡茶還是咖啡,都有共同的步驟:
- 煮沸水,相同點
- 沸水+原料(不同點,咖啡,茶葉)
- 將飲料倒入杯子,相同點
- 加調(diào)料(不同點:糖與牛奶,檸檬)
可以抽象一個父類繼續(xù)泡飲料的過程,代碼如下:
- class Beverage {
- init() {
- this.boilWater();
- this.brew();
- this.pourInCup();
- this.addCondiments();
- }
- boilWater() {
- console.log('煮沸水');
- }
- brew() {
- throw new Error( '子類必須重寫 brew 方法' );
- }
- pourInCup() {
- throw new Error( '子類必須重寫 pourInCup 方法' );
- }
- addCondiments() {
- throw new Error( '子類必須重寫 addCondiments 方法' );
- }
- }
因此泡咖啡可以重寫父類的方法,如下:
- class Coffee {
- brew() {
- console.log( '用沸水沖泡咖啡' );
- }
- pourInCup() {
- console.log( '把咖啡倒進杯子' );
- }
- addCondiments() {
- console.log( '加糖和牛奶' );
- }
- }
上述過程中,Beverage.prototype.init就是模板方法,它內(nèi)部封裝了子類的算法框架,它作為一個算法的模板,知道子類以何種順序執(zhí)行哪些方法
三、應(yīng)用場景
在構(gòu)建一系列的UI組件,這些組件的構(gòu)建過程一般如下:
- 初始化一個 div 容器
- 通過 ajax 請求拉取響應(yīng)的數(shù)據(jù)
- 把數(shù)據(jù)渲染到 div 容器里面,完成組件的構(gòu)造
- 通知用戶組件渲染完畢
于是,可以把這四個步驟都抽象到父類的模板方法里面,父類還可以順便提供第一步和第四步的具體實現(xiàn)。當子類繼承這個父類后,只需要重寫模板方法里面的 第二步和第三步即可
使用模板方法模式意味著子類放棄了對自己的控制權(quán),而是改為父類通知子類。作為子類,只負責提供一些設(shè)計上的細節(jié)
在傳統(tǒng)的編程語言中,子類的方法種類以及執(zhí)行順序都是不變的,這部分邏輯我們都抽象到了父類中,而子類的方法具體怎么實現(xiàn)是可變的,通過重寫父類的方法,將變化的邏輯部分封裝到子類中
通過增加新的子類,我們能夠給系統(tǒng)增加新的功能的,俺是并不需要修改父類以及其他的子類,這也符合開放-封閉原則
在javascript中,我們不需要依樣畫瓢去實現(xiàn)一個模板方法模式,因為高階函數(shù)是一個更好的選擇
參考文獻
https://refactoringguru.cn/design-patterns/template-method
https://zhuanlan.zhihu.com/p/129248167
https://juejin.cn/post/6844903615476269064