定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
真实世界类比
模板方法可用于建造大量房屋。 标准房屋建造方案中可提供几个扩展点, 允许潜在房屋业主调整成品房屋的部分细节。
每个建造步骤 (例如打地基、 建造框架、 建造墙壁和安装水电管线等) 都能进行微调, 这使得成品房屋会略有不同。
实现方式
模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。
abstract class AbstractClass {
/**
* The template method defines the skeleton of an algorithm.
*/
public templateMethod(): void {
this.baseOperation();
this.requiredOperations();
}
/**
* These operations already have implementations.
*/
protected baseOperation(): void {
console.log('AbstractClass says: I am doing the bulk of the work');
}
/**
* These operations have to be implemented in subclasses.
*/
protected abstract requiredOperations(): void;
}
class ConcreteClass extends AbstractClass {
protected requiredOperations(): void {
console.log('ConcreteClass1 says: Implemented Operation');
}
protected requiredOperation(): void {
console.log('ConcreteClass1 says: Implemented Operation2');
}
}
Coffee or Tea
泡咖啡的步骤:
(1) 把水煮沸
(2) 用沸水冲泡咖啡
(3) 把咖啡倒进杯子
(4) 加糖和牛奶
泡茶的步骤:
(1) 把水煮沸
(2) 用沸水浸泡茶叶
(3) 把茶水倒进杯子
(4) 加柠檬
大体过程类似,细节稍有不同。
提取抽象过程:
(1) 把水煮沸
(2) 用沸水冲泡饮料
(3) 把饮料倒进杯子
(4) 加调料
根据抽象过程书写抽象构造函数:
class Beverage {
// 烧水
boilWater() { console.log('把水煮沸') }
// 冲泡
// 空方法,应该由子类重写
brew() {
throw new Error('子类必须重写brew方法')
}
// 倒进杯子
// 空方法,应该由子类重写
pourInCup() {
throw new Error('子类必须重写pourInCup方法')
}
// 加调料
// 空方法,应该由子类重写
addCondiments() {
throw new Error('子类必须重写addCondiments方法')
}
// 模板方法框架
// 定义执行顺序
init() {
this.boilWater()
this.brew()
this.pourInCup()
this.addCondiments()
}
}
实现Coffee:
class Coffee extends Beverage {
brew() {
console.log('用沸水冲泡咖啡')
}
pourInCup() {
console.log('把咖啡倒进杯子')
}
addCondiments() {
console.log('加糖和牛奶')
}
}
const coffee = new Coffee()
coffee.init()
实现Tea:
class Tea extends Beverage {
brew() {
console.log('用沸水浸泡茶叶')
}
pourInCup() {
console.log('把茶水倒进杯子')
}
addCondiments() {
console.log('加柠檬')
}
}
const tea = new Tea()
tea.init()
适用场景
模板方法模式常被架构师用于搭建项目的框架,架构师定好了框架的骨架,程序员继承框架的结构之后,负责往里面填空
小结
模板方法模式是一种典型的通过封装变化提高系统扩展性的设计模式。在传统的面向对象语言中,一个运用了模板方法模式的程序中,子类的方法种类和执行顺序都是不变的,所以我们把这部分逻辑抽象到父类的模板方法里面。而子类的方法具体怎么实现则是可变的,于是我们把这部分变化的逻辑封装到子类中。通过增加新的子类,我们便能给系统增加新的功能,并不需要改动抽象父类以及其他子类,这也是符合开放-封闭原则的。缺点是模板方法中的步骤越多, 其维护工作就可能会越困难。