设计模式实战-模板模式,代码复用神器

时间:2022-07-22
本文章向大家介绍设计模式实战-模板模式,代码复用神器,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1、定义

模板模式(Template Pattern)又被称作模板方法模式(Template Method Pattern),它是一种简单的、常见的且应用非常广泛的模式。

英文定义如下:

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.

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

简单来说,就是为子类设计一个模板,以便在子类中可以复用这些方法。

2、组成角色

模板模式包含如下角色:

  • 抽象模板(Abstract Template)角色:该角色定义一个或多个抽象操作,以便让子类实现;这些抽象操作是基本操作,是一个顶级逻辑的组成步骤,该角色还需要定义一个或几个模板方法(模板方法的介绍,请看下文);
  • 具体模板(Concrete Template)角色:该角色实现抽象模板中定义的一个或多个抽象方法,每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法的不同实现,从而使得顶级逻辑的实现各不相同。

角色之间的 UML 关系图如下:

3、模板模式代码实现

3.1 抽象模板

/**
 * 抽象模板
 */
abstract class AbstractTemplate {
    // 模板方法
    public void templateMethod() {
        //(所有)基本方法
        abstractMethod();
        hookMethod();
        concreteMethod();
    }
    // 抽象方法,必须有子类实现
    protected abstract void abstractMethod();
    // 钩子方法,子类可以选择重写或不重写
    protected void hookMethod() {
    }
    // 具体方法,子类不可修改
    private final void concreteMethod() {
        System.out.println("抽象类中的具体方法");
    }
}

3.2 具体模板 A

/**
 * 具体模板 A 类
 */
class ConcreteTemplateA extends AbstractTemplate {
    @Override
    protected void abstractMethod() {
        System.out.println("A 子类中重写的抽象方法");
    }
}

3.3 具体模板 B

/**
 * 具体模板 B 类
 */
class ConcreteTemplateB extends AbstractTemplate {
    @Override
    protected void abstractMethod() {
        System.out.println("B 子类中重写的抽象方法");
    }
}

3.4 调用

public class Client {
    public static void main(String[] args) {
        AbstractTemplate tp = new ConcreteTemplateA();
        // 调用子类 A 的模板方法
        tp.templateMethod();
        tp = new ConcreteTemplateB();
        // 调用子类 B 的模板方法
        tp.templateMethod();
    }
}

程序执行结果如下:

A 子类中重写的抽象方法 抽象类中的具体方法 B 子类中重写的抽象方法 抽象类中的具体方法

从以上代码可以看出,在模板模式中,方法分为两类:模版方法和基本方法,而基本方法又分为:抽象方法,具体方法,钩子方法。

  • 抽象方法:一个抽象方法由抽象类声明,由具体子类实现。在 Java 语言里抽象方法以 abstract 关键字声明;
  • 具体方法:一个具体方法由抽象类声明并实现,而子类并不能修改或重写,此方法通常会被声明为 final;
  • 钩子方法:在抽象类中预留一个 “钩子”,也就是实现一个空方法,作为方法的默认实现,子类可以选择重写(重新构建)或者不重写。

小贴士:一个模板方法是定义在抽象类中的,把基本操作方法组合在一起形成一个总算法或一个总行为的方法。 一个抽象类可以有任意多个模板方法,而不限于一个,每一个模板方法都可以调用任意多个具体方法。

4、优缺点

模板模式的优点:

  • 提高了代码的复用性,将相同部分的代码放在抽象的父类中;
  • 提高了拓展性:将不同的代码放入不同的子类中,通过对子类的扩展增加新的行为;
  • 符合开闭原则:行为由父类控制,通过子类扩展新的行为。

模板模式的缺点:

  • 每个不同的行为都要新增一个子类来完成,抽象类中的抽象方法越多,子类增加成本就越高。而且新增的子类越多,系统就越复杂。

5、应用场景

模板模式的典型应用场景如下:

  • 多个子类有公共方法,并且逻辑基本相同时;
  • 可以把重要的、复杂的、核心算法设计为模板方法,其他的相关细节功能则由各个子类实现;
  • 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为。

6、使用实例

以生活中上班的过程为例,我们上班的通常流程是:起床洗漱 -> 通勤(开车、坐公交、打车) -> 到达公司。从以上步骤可以看出,只有通勤部分是不一样的,其他都一样,因为开车可能会被限号,就只能打车或坐公交去公司了,下面我们用代码(模板模式)来实现一下。

6.1 上班抽象类

/**
 * 上班抽象(模板)类
 */
abstract class AbstractWork {
    // 模板方法
    public void gotoWork() {
        getup();
        commute();
        arrive();
    }
    // 起床洗漱
    public void getup() {
        System.out.println("1.起床洗漱");
    }
    // 通勤
    abstract void commute();
    // 到达公司
    public void arrive() {
        System.out.println("3.到达公司");
    }
}

6.2 开车上班

/**
 * 开车上班
 */
class DriveToWork extends AbstractWork {

    @Override
    void commute() {
        System.out.println("2.开车去公司");
    }
}

6.3 坐公交上班

/**
 * 坐公交上班
 */
class BusToWork extends AbstractWork {

    @Override
    void commute() {
        System.out.println("2.坐公交去公司");
    }
}

6.4 客户端

public class Client {
    public static void main(String[] args) {
        AbstractWork work = new DriveToWork();
        // 开车上班
        work.gotoWork();
        work = new BusToWork();
        // 坐公交上班
        work.gotoWork();
    }
}

程序执行结果如下:

1. 起床洗漱 2. 开车去公司 3. 到达公司 1. 起床洗漱 2. 坐公交去公司 3. 到达公司

7、总结

模板模式的精髓是复用抽象类中的公共方法,重写抽象类中的基础(抽象)方法,选择性使用抽象类中的钩子(hook Method)方法。使用模板模式的关键是:子类可以置换掉父类的可变部分,但是子类却不可以改变模板方法所代表的顶级逻辑。