模版方法模式通过封装提高系统扩展性,把不变的逻辑抽象到父类,子类继承父类方法,子类逻辑方法是可变的。通过增加新的子类,就可以增加新的功能,并不需要改动抽象父类以及其他子类
只需使用继承就可以实现,把相同的逻辑抽离到单一的地方,模版方法就是为解决这个问题而生的。模版方法中,子类实现中的相同部分被上移到父类中,而将不同的部分留在子类实现。
JavaScript 没有从语法层面提供对抽象类的支持。抽象类的第一个作用时隐藏对象的具体类型,由于 JavaScript 是一门“类型模糊”的语言,所以隐藏对象的类型在 JavaScript 中并不重要。
在子类中如果我们忘记写 x 方法,那么请求会顺着原型链从父类找到对应的方法 x,而父类中 x 方法只是一个空方法。
解决方法
Father.prototype.brew = function () {
throw new Error("必须重写 brew 方法");
};
```javascript
/**
* 泡咖啡
* 1. 把水煮沸
* 2. 用沸水冲泡咖啡
* 3. 把咖啡倒进杯子
* 4. 加糖和牛奶
*/
const Coffee = function () {};
Coffee.prototype.boilWater = function () {
console.log("把水煮沸");
};
Coffee.prototype.brewCoffeeGriends = function () {
console.log("用沸水冲泡咖啡");
};
Coffee.prototype.pourInCup = function () {
console.log("把咖啡倒进杯子");
};
Coffee.prototype.addSugarAndMilk = function () {
console.log("加糖和牛奶");
};
Coffee.prototype.init = function () {
this.boilWater();
this.brewCoffeeGriends();
this.pourInCup();
this.addSugarAndMilk();
};
const Coffee1 = new Coffee();
Coffee1.init();
/**
* 泡茶
* 1. 把水煮沸
* 2. 用沸水冲泡茶
* 3. 把茶倒进杯子
* 4. 加柠檬
*/
const Tea = function () {};
Tea.prototype.boilWater = function () {
console.log("把水煮沸");
};
Tea.prototype.brewCoffeeGriends = function () {
console.log("用沸水冲泡茶");
};
Tea.prototype.pourInCup = function () {
console.log("把茶倒进杯子");
};
Tea.prototype.addSugarAndMilk = function () {
console.log("加柠檬");
};
Tea.prototype.init = function () {
this.boilWater();
this.brewCoffeeGriends();
this.pourInCup();
this.addSugarAndMilk();
};
const Tea1 = new Tea();
Tea1.init();
/**
* 分离出共同点
* 不同点:
* 原料不同:咖啡/茶
* 泡的方式不同,咖啡冲泡,茶侵泡。简称为泡
* 加入调料不同,糖牛奶/柠檬,简称为调料
*/
class CoffeTea {
constructor() {}
boilWater() {
console.log("把水煮沸");
}
brew() {}
pour() {}
add() {}
init() {
this.boilWater();
this.brew();
this.pour();
this.add();
}
}
const Coffee2 = new CoffeTea();
Coffee2.brew = function () {
console.log(1);
};
Coffee2.pour = function () {
console.log(2);
};
Coffee2.add = function () {
console.log(3);
};
Coffee2.init();
```
通过模版方法模式,我们在父类中封装了子类的算法框架,这些算法框架在正常情况下时适用于大多数子类的。但如果有一些特别的子类,如何让这些子类不受这个约束呢。钩子方法可以解决这个问题,放置钩子函数时隔离变化的一种常见手段,我们在父类中容易变化的地方放置钩子,钩子可以有一个默认的实现,子类决定要不要挂钩。
class bb {
constructor() {}
boil() {
console.log(1);
}
add() {
console.log("add");
}
isAdd() {
return true;
}
init() {
this.boil();
if (this.isAdd()) {
this.add();
}
}
}
const bbbb1 = new bb();
bbbb1.boil = function () {
console.log("boil");
};
bbbb1.isAdd = function () {
return false;
};
bbbb1.init();