• JavaScript设计模式中的模版方法模式


    模版方法模式

    模版方法模式通过封装提高系统扩展性,把不变的逻辑抽象到父类,子类继承父类方法,子类逻辑方法是可变的。通过增加新的子类,就可以增加新的功能,并不需要改动抽象父类以及其他子类

    只需使用继承就可以实现,把相同的逻辑抽离到单一的地方,模版方法就是为解决这个问题而生的。模版方法中,子类实现中的相同部分被上移到父类中,而将不同的部分留在子类实现。

    模版方法由两部分结构组成:

    1. 第一部分抽象父类:通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法执行顺序。
    2. 第二部分是具体的实现子类:继承这个抽象类,也继承整个算法结构,并且可以选择重写父类的方法;

    抽象类

    1. 模版方法模式严重依赖抽象类的设计模式
    2. 类分为两种:具体类和抽象类
      1. 具体类: 可以被实例化,也就是指向具体对象,比如可乐就是具体类
      2. 抽象类:不能被实例化,是被某些具体类继承的。比如饮料就可以当成抽象类,饮料分为很多种。
        1. 作用:为子类定义这些公共接口(方法)

    抽象方法和具体方法

    1. 抽象方法被声明在抽象类中,抽象方法并没有具体的实现方法,使一些 哑 方法。比如 brew、pour 都是抽象方法。当子类继承了这个抽象类时,必须重写父类的抽象方法。

    JavaScript 没有抽象类的缺点和解决方案

    1. JavaScript 没有从语法层面提供对抽象类的支持。抽象类的第一个作用时隐藏对象的具体类型,由于 JavaScript 是一门“类型模糊”的语言,所以隐藏对象的类型在 JavaScript 中并不重要。

    2. 在子类中如果我们忘记写 x 方法,那么请求会顺着原型链从父类找到对应的方法 x,而父类中 x 方法只是一个空方法。

      1. 在 Java 中编译器会保证重写父类的抽象方法。但 JavaScript 没有进行这些检查工作,得不到任何警告。
    3. 解决方法

      1. 鸭子类型 [https://zhuanlan.zhihu.com/p/406675760] 模拟接口检查,以便确保子类中确实重写了父类的方法。但模拟接口需要添加一些跟业务无关的代码,带来不必要的复杂性
      2. 在父类定义方法的时候,抛出一个异常
      Father.prototype.brew = function () {
        throw new Error("必须重写 brew 方法");
      };
      
      • 1
      • 2
      • 3
       ```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();
       ```
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
      • 86
      • 87
      • 88
      • 89
      • 90
      • 91
      • 92
      • 93
      • 94
      • 95
      • 96
      • 97
      • 98
      • 99
      • 100
      • 101
      • 102
      • 103
      • 104
      • 105
      • 106
      • 107
      • 108

    模版方法模式的使用场景

    1. 从发的方面讲,模版方法模式常被架构师用于搭建项目的框架,架构师定好了框架的骨架,程序员继承框架的结构之后,负责往里面填空。

    钩子方法

    1. 通过模版方法模式,我们在父类中封装了子类的算法框架,这些算法框架在正常情况下时适用于大多数子类的。但如果有一些特别的子类,如何让这些子类不受这个约束呢。钩子方法可以解决这个问题,放置钩子函数时隔离变化的一种常见手段,我们在父类中容易变化的地方放置钩子,钩子可以有一个默认的实现,子类决定要不要挂钩。

      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();
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
  • 相关阅读:
    密码CTF(4)——e和phi不互素
    py脚本解决ArcGIS Server服务内存过大的问题
    061:vue+openlayer画点、线、圆、多边形( 代码示例 )
    GSCoolink GSV9001 HDMI2to1 Video Processor
    工作项目:我要买G!T!R!
    某些之前的漏洞的遗忘的记录
    算法:(三)字符串
    [数据结构与算法] 线性表之单链表初始化详解
    「Java开发指南」如何在Spring中使用JAX-WS注释器?
    win10 修改MySQL8 wait_timeout 等超时和连接数配置
  • 原文地址:https://blog.csdn.net/weixin_43405946/article/details/127681129