前端设计模式应用--模板模式

142 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天

介绍

  • 在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。

    定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

    一个抽象类中,有一个主方法,再定义1…n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用。

    模板方法模式是一种只需使用继承就可以实现的非常简单的模式。

使用场景

  • 有多个子类共有的方法,且逻辑相同。
  • 重要的、复杂的方法,可以考虑作为模板方法。
  • 在软件设计中,有些功能很类似,只是在某些环节不同而已。大多数环节都是相同的时候,可以使用模板模式。将通用的算法或者步骤抽取到抽象类中,在具体子类中实现具体特定的操作 

应用实践

职责明确

  • abstract 类,其中封装了执行顺序的方法,以及每一步要执行的抽象方法;
  • 子类,实现了各种方法,最后调用父类中 执行顺序 的方法。

实现

简单来说,模版模式就是抽象父类提供一个骨架方法,里边会调用一些抽象方法或者空方法,抽象方法/空方法由子类自行去实现,可以看一下 UML 类图。

模板模式有个经典的例子就是: 咖啡与茶

// ts 定义一个接口
interface IMakeDrinks {
  /* 烧开水 */
  boilWater: () => void;
  /* 泡东西 */
  brew: () => void;
  /* 倒入杯子 */
  pourInCup: () => void;
  /* 加调料 */
  addCondiments: () => void;
  /* 初始化方法 */
  init: () => void
}

/**
 * 做饮料父类
 */
class AbstractMakeDrinks implements IMakeDrinks {

    isAddCondiments: boolean = false;

    constructor(isAddCondiments: boolean) {
        this.isAddCondiments = isAddCondiments;
    }

    addCondiments() {
        throw new Error('需要子类实现')
    }

    boilWater() {
        throw new Error('需要子类实现')
    }

    brew() {
        throw new Error('需要子类实现')
    }

    pourInCup() {
        throw new Error('需要子类实现')
    }

    init() {
        this.boilWater();
        this.brew();
        this.pourInCup();
        if (this.isAddCondiments) {
            this.addCondiments();
        }
    }

}

class Coffee extends AbstractMakeDrinks {

    boilWater() {
        console.log('把白开水烧开')
    }

    brew() {
        console.log('把咖啡和水导入壶里')
    }

    pourInCup() {
        console.log('倒入咖啡杯')
    }

    addCondiments() {
        console.log('加牛奶和糖')
    }

}

const coffee = new Coffee(true);
coffee.init();

汇总

虽然在 js 中我们并不能真正实现模版模式,但模版模式的作用我们还是实现了,践行了「开放关闭原则」:

  • 对扩展开放: 可以通过传入不同的参数,实现不同的应用需求。

  • 对修改关闭: 模版方法通过闭包的形式,内部的属性、方法外界并不能修改。

模版方法同样提升了复用能力,我们可以把公共的部分提取到模版方法中,业务方就不需要自己再实现一次了。

优点特性

  • 利用模板方法将相同处理逻辑的代码放到抽象父类中,可以提高代码的复用性。

  • 将不同的代码不同的子类中,通过对子类的扩展增加新的行为,提高代码的扩展性。

  • 把不变的行为写在父类上,去除子类的重复代码,提供了一个很好的代码复用平台,符合开闭原则

缺点弊端

  • 类数目的增加,每一个抽象类都需要一个子类来实现,这样导致类的个数增加。

  • 类数量的增加,间接地增加了系统实现的复杂度。

  • 继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍。