TypeScript 设计模式之模板方法

1、简介

模板方法模式是一种只需使用继承就能够实现的很是简单的模式。模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。一般在抽象父类中封装了子类的算法框架,也包括实现一些公共方法以及封装子类中全部方法的执行顺序。子类经过继承这个抽象类,也继承了整个算法结构,而且能够选择重写父类的方法。算法

2、优缺点

优势

  • 在父类中形式化地定义一个算法,而由它的子类来实现细节的处理,在子类实现详细的处理算法时并不会改变算法中步骤的执行次序。
  • 模板方法模式是一种代码复用技术,它在类库设计中尤其重要,它提取了类库中的公共行为,将公共行为放在父类中,而经过子类来实现不一样的行为,它鼓励咱们恰当使用继承来实现代码复用。
  • 可实现一种反向控制结构,经过子类覆盖父类的钩子方法来决定某一特定步骤是否须要执行。
  • 在模板方法模式中能够经过子类来覆盖父类的基本方法,不一样的子类能够提供基本方法的不一样实现,更换和增长新的子类很方便,符合单一职责原则和开闭原则。

缺点

  • 须要为每个基本方法的不一样实现提供一个子类,若是父类中可变的基本方法太多,将会致使类的个数增长,系统更加庞大,设计也更加抽象。此时,能够结合桥接模式来进行设计。

3、应用场景

  • 一个类不知道它所须要的对象的类:在工厂方法模式中,客户端不须要知道具体产品类的类名,只须要知道所对应的工厂便可,具体的产品对象由具体工厂类建立;客户端须要知道建立具体产品的工厂类。
  • 一个类经过其子类来指定建立哪一个对象:在工厂方法模式中,对于抽象工厂类只须要提供一个建立产品的接口,而由其子类来肯定具体要建立的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。

4、模式结构

模板方法模式包含如下角色:typescript

  • AbstractClass(抽象类):在抽象类中定义了一系列基本操做 (PrimitiveOperations),这些基本操做能够是具体的,也能够是抽象的,每个基本操做对应算法的一个步骤,在其子类中能够重定义或实现这些步骤。同时,在抽象类中实现了一个模板方法,用于定义一个算法的框架,模板方法不只能够调用在抽象类中实现的基本方法,也能够调用在抽象类的子类中实现的基本方法,还能够调用其余对象中的方法。
  • ConcreteClass(具体子类):它是抽象类的子类,用于实如今父类中声明的抽象基本操做以完成子类特定算法的步骤,也能够覆盖在父类中已经实现的具体基本操做。

template-method-uml.jpg

5、实战

具体实现

定义 AbstractClass 抽象类segmentfault

class AbstractClass {
    public method1(): void {
        throw new Error("Abstract Method");
    }

    public method2(): void {
        throw new Error("Abstract Method");
    }

    public method3(): void {
        throw new Error("Abstract Method");
    }

    public templateMethod(): void {
        console.log("templateMethod is being called");
        this.method1();
        this.method2();
        this.method3();
    }
}

定义 ConcreteClass 类设计模式

class ConcreteClass extends AbstractClass {
    public method1(): void {
        console.log("method1 of ConcreteClass");
    }

    public method2(): void {
        console.log("method2 of ConcreteClass");
    }

    public method3(): void {
        console.log("method3 of ConcreteClass");
    }
}

使用示例

function show(): void {
    const cc: ConcreteClass = new ConcreteClass();
    cc.templateMethod();
}

以上代码成功运行后会输出如下结果:框架

templateMethod is being called
method1 of ConcreteClass
method2 of ConcreteClass
method3 of ConcreteClass

下面为了让小伙伴们能更好地理解模板方法设计模式,咱们来以 “泡咖啡与泡茶” 这个经典的例子来深刻介绍模板方法。this

首先,咱们先来泡一杯咖啡,泡咖啡的步骤一般以下:spa

  1. 把水煮沸
  2. 用沸水冲泡咖啡
  3. 把咖啡倒进杯子
  4. 加糖和牛奶

接下来,开始准备咱们的茶,泡茶的步骤跟泡咖啡的步骤相似:设计

  1. 把水煮沸
  2. 用沸水浸泡茶叶
  3. 把茶水倒进杯子
  4. 加柠檬

根据上面泡咖啡和泡茶的步骤,咱们能够整理出如下表格:code

泡咖啡步骤 泡茶步骤
把水煮沸 把水煮沸
用沸水冲泡咖啡 用沸水浸泡茶叶
把咖啡倒进杯子 把茶水倒进杯子
加糖和牛奶 加柠檬

咱们能够发现泡咖啡和泡茶主要有如下不一样点:对象

  • 原料不一样。一个是咖啡,一个是茶,但咱们能够把它们都抽象为 “饮料”。
  • 泡的方式不一样。咖啡是冲泡,而茶叶是浸泡,咱们能够把它们都抽象为 “泡”。
  • 加入的调料不一样。一个是糖和牛奶,一个是柠檬,但咱们能够把它们都抽象为 “调料”。

通过抽象以后,不论是泡咖啡仍是泡茶,咱们都能整理为下面四步:

  1. 把水煮沸
  2. 用沸水冲泡饮料
  3. 把饮料倒进杯子
  4. 加调料

因此,不论是冲泡仍是浸泡,咱们都能给它一个新的方法名称,好比说 brew()。同理,不论是加糖和牛奶,仍是加柠檬,咱们均可以称之为 addCondiments()

如今咱们能够建立一个抽象父类来表示泡一杯饮料的整个过程。不管是咖啡(Coffee),仍是茶(Tea),都被咱们用饮料(Beverage)来表示,最后咱们来看一下具体实现。

coffee-and-tea.jpg

建立 Beverage 抽象类

abstract class Beverage {
    boilWater() {
        console.log("把水煮沸");
    }

    abstract brew(): void;
    abstract pourInCup(): void;
    abstract addCondiments(): void;

    // 模板方法
    makeBeverage() {
        this.boilWater();
        this.brew();
        this.pourInCup();
        this.addCondiments();
    }
}

建立 Coffee 类

class Coffee extends Beverage {
    brew(): void {
        console.log("用沸水冲泡咖啡");
    }
    pourInCup(): void {
        console.log("把咖啡倒进杯子");
    }
    addCondiments(): void {
        console.log("加糖和牛奶");
    }
}

建立 Tea 类

class Tea extends Beverage {
    brew(): void {
        console.log("用沸水浸泡茶叶");
    }
    pourInCup(): void {
        console.log("把茶倒进杯子");
    }
    addCondiments(): void {
        console.log("加柠檬");
    }
}

使用示例

function show(): void {
    const coffee: Coffee = new Coffee();
    const tea: Tea = new Tea();
    coffee.makeBeverage();
    tea.makeBeverage();
}

以上代码成功运行后会输出如下结果:

把水煮沸
用沸水冲泡咖啡
把咖啡倒进杯子
加糖和牛奶
把水煮沸
用沸水浸泡茶叶
把茶倒进杯子
加柠檬

6、总结

在模板方法设计模式中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类能够按须要重写方法实现,但调用将以抽象类中定义的方式进行。模板方法定义一个操做中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类能够不改变一个算法的结构便可重定义该算法的某些特定步骤。

本人的全栈修仙之路订阅号,会按期分享 Angular、TypeScript、Node.js/Java 、Spring 相关文章,欢迎感兴趣的小伙伴订阅哈!

full-stack-logo

相关文章
相关标签/搜索