模板模式

108 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第28天,点击查看活动详情

导言

从上篇观察者模式开始,我们就进入了行为型设计模式。本篇继续第二种行为型设计模式:模板模式。

模板模式是一种基于继承的设计模式。主要由两部分结构组成:抽象父类和具体的实现子类。在抽象父类中封装子类的算法框架,包括实现一些公共方法以及封装子类中所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。

如果我们有一些平行的子类,各个子类之间有相同的行为,也有一些不同的行为。如果相同和不同的行为都由各个子类独自实现的话,那就是无用功,写了重复的逻辑代码,我们应该把相同的行为抽象出来,放到公共父类中或其他特定的地方,这时候,模板模式就应运而生了。

模板模式

我们来看个咖啡与茶的例子,也是经常用来讲解模板模式的经典例子,这个例子的原型来自《Head First设计模式》。

泡一杯咖啡

步骤:

  • 烧一壶开水
  • 用开水泡咖啡
  • 把咖啡倒进杯子
  • 加糖和牛奶

用代码来实现:

let Coffee = function () {}
Coffee.prototype.boilWater = function () {
  console.log('烧一壶开水')
}
Coffee.prototype.brewCoffee = function () {
  console.log('用开水泡咖啡')
}
Coffee.prototype.pourInCup = function () {
  console.log('把咖啡倒进杯子')
}
Coffee.prototype.addSugarAndMilk = function () {
  console.log('加糖和牛奶')
}
Coffee.prototype.init = function () {
  this.boilWater()
  this.brewCoffee()
  this.pourInCup()
  this.addSugarAndMilk()
}
let coffee = new Coffee()
coffee.init() // 一杯咖啡泡好了

泡一杯茶

步骤:

  • 烧一壶开水
  • 用开水浸泡茶叶
  • 把茶水倒进杯子
  • 加柠檬

用代码来实现:

let Tea = function () {}
Tea.prototype.boilWater = function () {
  console.log('烧一壶开水')
}
Tea.prototype.brewTea = function () {
  console.log('用开水浸泡茶叶')
}
Tea.prototype.pourInCup = function () {
  console.log('把茶水倒进杯子')
}
Tea.prototype.addLemon = function () {
  console.log('加柠檬')
}
Tea.prototype.init = function () {
  this.boilWater()
  this.brewTea()
  this.pourInCup()
  this.addLemon()
}
let tea = new Tea()
tea.init() // 一杯茶泡好了

咖啡和茶都泡好了,现在我们遵循模板设计模式来提取公共部分作为模板。

步骤一是相同的,可以提取到父类构造函数;步骤二原料是不一样的,分别是咖啡和茶叶,可以抽象成饮料;步骤三也是倒的动作一直,不同的还是饮料;步骤四咖啡是加糖和牛奶,茶是加柠檬,可以抽象成调料。

let Beverage = function () {}
Beverage.prototype.boilWater = function () {
  console.log('烧一壶开水')
}
Beverage.prototype.brew = function () {}
Beverage.prototype.pourInCup = function () {}
Beverage.prototype.addCondiments = function () {}
Beverage.prototype.init = function () {
  this.boilWater()
  this.brew()
  this.pourInCup()
  this.addCondiments()
}

// 创建子类

// 咖啡
let Coffee = function () {}
Coffee.prototype = new Beverage()
Coffee.prototype.brew = function () {
  console.log('用开水泡咖啡')
}
Coffee.prototype.pourInCup = function () {
  console.log('把咖啡倒进杯子')
}
Coffee.prototype.addCondiments = function () {
  console.log('加糖和牛奶')
}
let coffee = new Coffee()
coffee.init()

// 茶
let Tea = function () {}
Tea.prototype = new Beverage()
Tea.prototype.brew = function () {
  console.log('用开水泡茶')
}
Tea.prototype.pourInCup = function () {
  console.log('把茶倒进杯子')
}
Tea.prototype.addCondiments = function () {
  console.log('加柠檬')
}
let tea = new Tea()
tea.init()

先创建个类似父类的构造函数,把公共的步骤方法放入其中,并且对不同的步骤进行抽象,定义出关键方法来形成总体的架构并且可供子类进行重写。

模板模式是一种典型的通过封装公共的和抽象出变化来提高系统扩展性的设计模式。但在JavaScript中,我们很多时候也不一定要这样依葫芦画瓢地去实现一个模版模式,高阶函数也是一种选择选择。