定义
模板方式是一种只需要继承就可以实现的简单的模式
模板由两个部分结构组成,第一个是抽象类,第二部分是具体的实现子类。
通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法的执行顺序。
子类通过继承这个抽象父类,也继承了算法框架,并且可以选择重写父类。
举个栗子 - Coffee or Tea
- 泡一杯咖啡
泡咖啡的步骤通常如下:
1)把水煮沸
2)用沸水冲咖啡
3)把咖啡倒进杯子
4)加糖或加奶
通过下面的代码我们就可以获得一杯咖啡
class Coffee {
init() {
inst.boilWater();
inst.brewCoffeeGriends();
inst.pourInCup();
inst.addSugarAndMilk();
}
boilWater() {
console.log("煮沸水");
}
brewCoffeeGriends() {
console.log("用沸水冲咖啡");
}
pourInCup() {
console.log("将咖啡倒进杯子");
}
addSugarAndMilk() {
console.log("加糖或加奶");
}
}
const inst = new Coffee();
inst.init();
- 泡一杯茶
泡茶的步骤通常如下:
1)把水煮沸
2)用沸水浸泡茶叶
3)把茶倒进杯子
4)加柠檬
class Tea {
init() {
this.boilWater();
this.steepTeaBag();
this.pourInCup();
this.addLemon();
}
boilWater() {
console.log("煮沸水");
}
steepTeaBag() {
console.log("用沸水浸泡茶叶");
}
pourInCup() {
console.log("把茶倒进杯子");
}
addLemon() {
console.log("加柠檬");
}
}
const inst = new Tea();
inst.init();
事实上,泡咖啡和泡茶的过程是大同小异的。我们可以发现,主要有以下几点不同:
原料不同。一个是咖啡,一个是茶叶,我们可以抽象成“饮料”
泡的方式不同,一个是冲泡,一个是浸泡。我们可以抽象为“泡”
加入的调料不同,一个是糖和牛奶,一个是柠檬。我们可以抽象为“调料”
经过抽象后,不管是泡咖啡还是泡茶,我们都抽象成为以下四个步骤:
1)把水煮沸
2)用沸水冲泡饮料
3)把饮料倒进杯子
4)在饮料中加入调料
所以,不管是冲泡还是浸泡,我们都可以给一个通用的方法 brew()。
同理,不管是糖还是牛奶还是柠檬,我们都可以称之 addCondiments()
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 和 Tea 子类
class Coffee extends Beverage {
brew() {
console.log("用沸水冲泡咖啡");
}
pourInCup() {
console.log("吧咖啡倒进杯子");
}
addCondiments() {
console.log("给咖啡加糖");
}
}
const coffee = new Coffee();
coffee.init();
class Tea extends Beverage {
brew() {
console.log("用沸水浸泡茶");
}
pourInCup() {
console.log("把茶倒进杯子");
}
addCondiments() {
console.log("加柠檬");
}
}
const tea = new Tea();
tea.init();
模板方法的应用场景
模板方法模式常被架构师用于搭建项目的框架,架构师定好了框架的骨架,
程序员继承框架的结构之后,负责往里面填空。
hook 方法
当某些子类有一些个性化的需求,比如这里,泡茶不想要加柠檬。
那么可以这样改写父类
class Beverage {
// ....
wantsAddCondiments() {
return true;
}
init() {
this.boilWater();
this.brew();
this.pourInCup();
if (this.wantsAddCondiments()) this.addCondiments();
}
}
class Tea extends Beverage {
brew() {
console.log("用沸水浸泡茶");
}
pourInCup() {
console.log("把茶倒进杯子");
}
wantsAddCondiments() {
return window.confirm("是否想加柠檬呢?");
}
addCondiments() {
console.log("加柠檬");
}
}
const tea = new Tea();
tea.init();