likes
comments
collection
share

模板方法设计模式在Js中的使用

作者站长头像
站长
· 阅读数 11

模板方法设计模式

定义

模板方法模式是一种行为型设计模式,它定义了一个算法的骨架,并允许子类在不改变算法结构的情况下重写算法中的某些步骤

参与者

  1. 抽象类(Abstract Class):抽象类定义了模板方法,该方法包含了算法的骨架,其中的具体步骤可能由子类实现。抽象类也可以提供一些默认的实现,以便子类选择性地覆盖。

  2. 具体类(Concrete Class):具体类是抽象类的子类,它实现了抽象类中定义的具体步骤。每个具体类都可以根据需要实现自己的逻辑,但不能改变算法的结构。

原理

在模板模式中,算法的骨架被定义在抽象类中的模板方法中,该方法按照固定的顺序调用了一系列的具体步骤。其中的一些步骤可能在抽象类中提供了默认的实现,但也可以由具体类进行覆盖。通过这种方式,模板模式使得算法的整体结构保持稳定,但允许具体步骤的灵活替换。

优点

在于它提供了一种标准化的算法结构,使得不同的子类可以共享和重用相同的骨架代码。它还能够将变化部分隔离在具体类中,使得算法的修改更加灵活。

应用场景

模板模式常用于以下情况:当多个类有相似的算法,但某些步骤可能有所不同,

或者希望定义一个算法的整体结构,但允许部分步骤由子类来实现时,模板模式是一种很好的选择。

例如,在编写web框架时,可以使用模板模式来定义通用的请求处理流程,但允许具体的处理细节由不同的控制器进行实现。

示例

class AbstractClass {
  templateMethod() {
    this.primitiveOperation1();
    this.primitiveOperation2();
  }

  abstract primitiveOperation1(): void;
  abstract primitiveOperation1(): void;
}

class ConcreteClassA extends AbstractClass {
  primitiveOperation1() {
    console.log("ConcreteClassA: Step 1");
  }

  primitiveOperation2() {
    console.log("ConcreteClassA: Step 2");
  }
}

class ConcreteClassB extends AbstractClass {
  primitiveOperation1() {
    console.log("ConcreteClassB: Step 1");
  }

  primitiveOperation2() {
    console.log("ConcreteClassB: Step 2");
  }
}

const classA = new ConcreteClassA();
classA.templateMethod(); // 输出:ConcreteClassA: Step 1, ConcreteClassA: Step 2

const classB = new ConcreteClassB();
classB.templateMethod(); // 输出:ConcreteClassB: Step 1, ConcreteClassB: Step 2

模板方法模式的实现和ts

在ts中,我实现一个抽象类S,此抽象类具有方法A和抽象方法B C,如果A中使用了B和C且保证S被继承的时候A不会被重写,则这样就实现了模板设计模式。

原生实现

  1. JavaScript 中的 Array.prototype.sort() 方法:这个方法使用了模板设计模式来实现排序算法。它定义了一个模板方法 compareFunction,该方法接受两个参数并返回一个比较结果。具体的排序逻辑由 compareFunction 方法的实现决定,可以根据需要进行自定义。

  2. 浏览器中的事件处理程序(Event Handlers):例如,当我们为某个元素绑定点击事件时,可以使用模板设计模式来定义事件处理程序。在事件处理函数中,可以预先定义一些通用的操作步骤,然后调用具体的事件处理逻辑。

  3. JavaScript 中的模块加载器(如 RequireJS、CommonJS):这些模块加载器使用模板设计模式来定义加载和注入模块的过程。它们提供了一种标准化的模块加载流程,但允许开发人员在特定的模块中实现自己的逻辑。

  4. XMLHttpRequest 对象:在使用 XMLHttpRequest 发送 AJAX 请求时,可以通过使用回调函数来实现模板设计模式。我们可以定义一个模板方法,在其中指定 AJAX 请求的一般流程和处理逻辑,然后在具体的回调函数中实现特定的业务逻辑。

业务场景

  • 见【模板方法模式的实现和ts】部分。事实上在平时的工作中,会不经意的实现模板设计模式,这种设计模式用的还是比较多的!
  • 常见的情况为:
    • 加载存档:json --> js的Object --> ui
    • 保存存档: ui --> js的object --> json
    • 需要对页面状态做持久化的存储的时候,会将渲染ui依据的配置对象序列化成json文件保存下来,在这个过程中,调用控制对象上的save方法;或者要加载已有的json文件,从中读取数据然后渲染到页面上的时候,调用控制对象上的render方法。这里的save和render方法就可以看成是模板方法,而save和render中调用的其他方法可以根据不同场景改变,但是save和render本身是不会变化的。
    • echarts中渲染函数setOption就属于这类。
    • 如下:可以看成渲染的模板已经写好,需要做的事情就是option的参数化渲染:
import * as echarts from 'echarts';

var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom);
var option;

option = {
  xAxis: {
    type: 'category',
    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  },
  yAxis: {
    type: 'value'
  },
  series: [
    {
      data: [150, 230, 224, 218, 135, 147, 260],
      type: 'line'
    }
  ]
};

option && myChart.setOption(option);

模板方法设计模式和外观设计模式

  • 在实现方式上,外观设计模式和模板设计模式的基本流程相同,但是侧重点不同;外观设计模式是一种结构型设计模式,强调的是信息传递使用的接口,而模板设计模式是一种行为型设计模式,强调的是信息的流动性

  • 其次,在实现上也有细微的差别,模板设计模式强调扩展类、集成、抽象接口这些概念,如果使用ts实现的话,最直观的特征就是有一个抽象类,实现了一个具体方法,这个方法调用了其他未实现的抽象方法,而这些抽象方法的实现要延迟到子类中去;而外观设计模式强调的是子系统,以及使子系统的复杂性不可见这个效果。

总结

模板设计模式在原生 JavaScript 和浏览器中的应用比较广泛。它允许我们定义算法的整体结构,并在特定的地方留出可自定义的空间,以适应不同的需求。通过这种方式,我们可以提高代码的复用性、可维护性和可扩展性。