设计模式-模板方法模式
2019-10-19 14:32:492022-09-05 21:27:09
WHAT
模板方法是一种只需要使用继承就可以实现的较为简单的模式。
模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法及封装子类中所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。
符合 OCP 原则
WHY
模板方法模式是一种典型的通过封装变化提高系统扩展性的设计模式。在传统的面向对象语 言中,一个运用了模板方法模式的程序中,子类的方法种类和执行顺序都是不变的,所以我们把 这部分逻辑抽象到父类的模板方法里面。而子类的方法具体怎么实现则是可变的,于是我们把这 部分变化的逻辑封装到子类中。通过增加新的子类,我们便能给系统增加新的功能,并不需要改动抽象父类以及其他子类,这也是符合开放-封闭原则的
HOW
分离共同点
写父类(子类的共同点),保证子类一定会实现(使用抛异常、抽象类/抽象方法、构造函数等来约束)
写子类,继承父类,实现父类的方法
传统继承实现
// 抛异常约束版
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();
}
}
class Coffee extends Beverage {
brew() {
console.log("用沸水冲泡咖啡");
}
pourInCup() {
console.log("把咖啡倒进杯子");
}
addCondiments() {
console.log("加糖和牛奶");
}
}
let coffee = new Coffee();
coffee.init();
高阶函数实现
javascript 中,函数是一等公民,可以直接将函数传递进 init 中
const beverage = (param) => {
const boilWater = () => {
console.log("把水煮沸");
}
const brew = param.brew || (() => {
throw new Error("应该传入brew方法");
})
const pourInCup = param.pourInCup || (() => {
throw new Error("应该传入pourInCup方法");
})
const addCondiments = param.addCondiments || (() => {
throw new Error("应该传入addCondiments方法");
})
/**
* 封装了饮料的算法框架
*/
const init = () => {
boilWater();
brew();
pourInCup();
addCondiments();
}
return {
init
}
}
const coffee = beverage({
brew: () => {
console.log('用沸水冲泡咖啡');
},
pourInCup: () => {
console.log('把咖啡倒进杯子');
},
addCondiments: () => {
console.log('加糖和牛奶');
},
})
coffee.init();
变化
兼容多种算法?有些子类的需要一些特殊的算法
钩子方法(hook)可以用来解决这个问题,放置钩子是隔离变化的一种常见手段。我们在父类中容易变化的地方放置钩子,钩子可以有一个默认的实现,究竟要不要“挂钩”,这由子类自 行决定。钩子方法的返回结果决定了模板方法后面部分的执行步骤,也就是程序接下来的走向