如何理解模板方法模式
准备食材(肉、蔬菜)
食材放到锅里
放调味料(糖、盐、油)
炒菜
倒到容器里(盘子、碗)
========================
先把水煮沸
冲泡饮料(咖啡、茶、牛奶)
倒进杯子中
最后加一些调味料(咖啡伴侣、糖)
在类似的场景中,这些例子都有这些特点:
有一个基本的操作流程,这个流程我们可以抽象出来,由具体实例的操作流程来实现,比如做咖啡的时候冲泡的就是咖啡,做茶的时候冲泡的就是茶;
一些共用的流程,就可以使用通用的公共步骤,比如把水煮沸,比如将食材放到锅里,这样的共用流程就可以共用一个具体方法就可以了;
**
模板方法模式:
**
abstract class AbstractClass {
constructor() {}
// 模板方法
public template() : void {
this.operation1();
this.hookMethod() && this.operation2();
this.operation3();
}
// 基本方法
protected operation1() : void {
console.log('使用了方法operation1');
}
protected operation2() : void {
console.log('使用了方法operation2');
}
protected operation3() : void {
console.log('使用了方法operation3');
}
// 钩子方法
protected hookMethod() : boolean {
return true;
}
}
class ConcreteClassA extends AbstractClass {
protected operation2() :void {
console.log('对该方法operation2进行了修改再使用');
}
protected operation3() :void {
console.log('对该方法operation3进行了修改再使用');
}
}
class ConcreteClassB extends AbstractClass {
// 覆盖钩子方法
protected hookMethod() : boolean {
return false;
}
}
function main() {
const class1 : AbstractClass = new ConcreteClassA();
const class2 : AbstractClass = new ConcreteClassB();
class1.template();
console.log('==========================')
class2.template();
}
main();
模板方法模式的优缺点
模板方法模式的优点:
- 封装了不变部分,扩展可变部分, 把算法中不变的部分封装到父类中直接实现,而可变的部分由子类继承后再具体实现;
- 提取了公共代码部分,易于维护, 因为公共的方法被提取到了父类,那么如果我们需要修改算法中不变的步骤时,不需要到每一个子类中去修改,只要改一下对应父类即可;
3.行为被父类的模板方法固定, 子类实例只负责执行模板方法,具备可扩展性,符合开闭原则;
模板方法模式的缺点:增加了系统复杂度,主要是增加了的抽象类和类间联系,需要做好文档工作;
其他相关模式
模板方法模式与工厂模式
模板方法模式的实现可以使用工厂模式来获取所需的对象。
另外,模板方法模式和抽象工厂模式比较类似,都是使用抽象类来提取公共部分,不一样的是:
- 抽象工厂模式 提取的是实例的功能结构;
- 模板方法模式 提取的是算法的骨架结构;
tip:另外一个更好的例子
/* 饮料类,父类 */
class Beverage {
constructor() {
if (new.target === Beverage) {
throw new Error('抽象类不能直接实例化!')
}
}
/* 烧开水,共用方法 */
boilWater() { console.log('水已经煮沸') }
/* 冲泡饮料,抽象方法 */
brewDrink() { throw new Error('抽象方法不能调用!') }
/* 倒杯子里,共用方法 */
pourCup() { console.log('倒进杯子里') }
/* 加调味品,抽象方法 */
addCondiment() { throw new Error('抽象方法不能调用!') }
/* 制作流程,模板方法 */
init() {
this.boilWater()
this.brewDrink()
this.pourCup()
this.addCondiment()
}
}
/* 咖啡类,子类 */
class Coffee extends Beverage {
constructor() { super() }
/* 冲泡饮料,实现抽象方法 */
brewDrink() { console.log('冲泡咖啡') }
/* 加调味品,实现抽象方法 */
addCondiment() { console.log('加点咖啡伴侣') }
}
const coffee = new Coffee()
coffee.init()
// 输出:水已经煮沸
// 输出:冲泡咖啡
// 输出:倒进杯子里
// 输出:加点咖啡伴侣