JavaScript设计模式中的模版方法模式
Posted 三水草肃
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript设计模式中的模版方法模式相关的知识,希望对你有一定的参考价值。
模版方法模式
模版方法模式通过封装提高系统扩展性,把不变的逻辑抽象到父类,子类继承父类方法,子类逻辑方法是可变的。通过增加新的子类,就可以增加新的功能,并不需要改动抽象父类以及其他子类
只需使用继承就可以实现,把相同的逻辑抽离到单一的地方,模版方法就是为解决这个问题而生的。模版方法中,子类实现中的相同部分被上移到父类中,而将不同的部分留在子类实现。
模版方法由两部分结构组成:
- 第一部分抽象父类:通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法执行顺序。
- 第二部分是具体的实现子类:继承这个抽象类,也继承整个算法结构,并且可以选择重写父类的方法;
抽象类
- 模版方法模式严重依赖抽象类的设计模式
- 类分为两种:具体类和抽象类
- 具体类: 可以被实例化,也就是指向具体对象,比如可乐就是具体类
- 抽象类:不能被实例化,是被某些具体类继承的。比如饮料就可以当成抽象类,饮料分为很多种。
- 作用:为子类定义这些公共接口(方法)
抽象方法和具体方法
- 抽象方法被声明在抽象类中,抽象方法并没有具体的实现方法,使一些 哑 方法。比如 brew、pour 都是抽象方法。当子类继承了这个抽象类时,必须重写父类的抽象方法。
javascript 没有抽象类的缺点和解决方案
-
JavaScript 没有从语法层面提供对抽象类的支持。抽象类的第一个作用时隐藏对象的具体类型,由于 JavaScript 是一门“类型模糊”的语言,所以隐藏对象的类型在 JavaScript 中并不重要。
-
在子类中如果我们忘记写 x 方法,那么请求会顺着原型链从父类找到对应的方法 x,而父类中 x 方法只是一个空方法。
- 在 Java 中编译器会保证重写父类的抽象方法。但 JavaScript 没有进行这些检查工作,得不到任何警告。
-
解决方法
- 鸭子类型 [https://zhuanlan.zhihu.com/p/406675760] 模拟接口检查,以便确保子类中确实重写了父类的方法。但模拟接口需要添加一些跟业务无关的代码,带来不必要的复杂性
- 在父类定义方法的时候,抛出一个异常
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();
以上是关于JavaScript设计模式中的模版方法模式的主要内容,如果未能解决你的问题,请参考以下文章