设计模式之模板方法模式
模板方法模式:由两部分结构组成,第一部分是抽象父类,第二部分是具体实现的子类。在抽象父类中封装了子类的算法框架,子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类方法。
应用场景
假如我们有一些平行的子类,各个子类之间有一些相同的行为,也有一些不同的行为,像这种情况就可以使用模板方法模式。在模板方法模式中,子类实现中的相同部分被上移到父类中,而将不同的部分留给子类自己实现
例子-咖啡与茶
泡咖啡的步骤通常如下:
- 把水煮沸
- 用沸水冲泡咖啡
- 把咖啡倒进杯子
- 加糖和牛奶
泡茶的步骤如下:
- 把水煮沸
- 用沸水浸泡茶叶
- 把茶叶倒进杯子
- 加柠檬
其实泡茶的步骤跟泡咖啡的步骤相差并不大,冲泡过程是大同小异的,找出两者的不同点,并将不同点转化为相同点:
- 糖和牛奶、柠檬相当于加入的调料不同,但我们可以把它们都抽象为”调料“
经过抽象之后,不管是泡咖啡还是泡茶,我们都能整理为以下四步:
- 把水煮沸
- 用沸水冲泡饮料
- 把饮料倒进杯子
- 加调料
具体代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| abstract class Beverage{ boilWater(){ console.log('把水煮沸') } abstract brew():void abstract pourInCup():void abstract addCondiments():void init(){ this.boilWater(); this.brew(); this.pourInCup(); this.addCondiments(); }
}
class Coffee extends Beverage{ constructor(){ super() } brew(): void { console.log('用沸水冲泡咖啡') } pourInCup(): void { console.log('把咖啡倒进杯子') } addCondiments(): void { console.log('加糖和牛奶') } }
class Tea extends Beverage{ constructor(){ super() } brew(): void { console.log('用水冲泡茶叶') } pourInCup(): void { console.log('把茶倒进杯子') } addCondiments(): void { console.log('加柠檬') } }
const coffee = new Coffee(); coffee.init() const tea = new Tea() tea.init()
|
钩子方法
在上述步骤中,我们规定了咖啡的制作为4个步骤。但有些客人喝咖啡是不加调料(糖和牛奶)的,这种情况又该怎么办?
钩子方法可以用来解决这个问题,放置钩子是隔离变化的一种常见手段。我们在父类中容易变化的地方放置钩子,钩子可以有一个默认的实现,究竟要不要”挂钩“,这由子类自行决定。钩子方法的返回结果决定了模板方法后面部分的执行步骤,也就是程序接下来的走向,这样一来,程序就拥有了变化的可能
加不加调料由客人决定
具体代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| abstract class Beverage{ abstract brew():void abstract pourInCup():void abstract addCondiments():void
boilWater(){ console.log('把水煮沸') } customerWantCondiments(){ return true } init(){ this.boilWater(); this.brew(); this.pourInCup(); if(this.customerWantCondiments()){ this.addCondiments(); } }
}
class Coffee extends Beverage{ constructor(){ super() } brew(): void { console.log('用沸水冲泡咖啡') } pourInCup(): void { console.log('把咖啡倒进杯子') } addCondiments(): void { console.log('加糖和牛奶') } customerWantCondiments(){ return window.confirm('请问需要调料吗?') } }
const coffee = new Coffee(); coffee.init()
|