【设计模式】一文速通模板模式

134 阅读3分钟

1. 引入

假设某次考试,试卷的题目相同,如何设计一个抄题目的功能。

针对上述功能,有以下两种方案实现。

1.1 方案一:利用继承实现

1.1.1 实现代码

  • 定义基类,基类中定义普通方法,可以被子类覆盖。
// 基类 TestPaper
class TestPaper {
    public void writeTestPaper() {
        gatherMaterials();
        readInstructions();
        writeAnswers();
        review();
    }

    // 普通方法,可以被子类覆盖
    protected void gatherMaterials() {
        System.out.println("收集试题材料。");
    }

    protected void readInstructions() {
        System.out.println("阅读试题说明。");
    }

    protected void writeAnswers() {
        System.out.println("写答案。");
    }

    protected void review() {
        System.out.println("复习检查。");
    }
}
  • 定义学生A具体子类,重写基类中的普通方法。
// 具体子类 StudentA
class StudentA extends TestPaper {
    @Override
    protected void gatherMaterials() {
        System.out.println("学生A收集了教科书和笔记。");
    }

    @Override
    protected void readInstructions() {
        System.out.println("学生A仔细阅读了试题说明。");
    }

    @Override
    protected void writeAnswers() {
        System.out.println("学生A根据笔记写下了答案。");
    }

    @Override
    protected void review() {
        System.out.println("学生A复习检查了答案。");
    }
}
  • 定义学生B具体子类,重写基类中的普通方法。
// 另一具体子类 StudentB
class StudentB extends TestPaper {
    @Override
    protected void gatherMaterials() {
        System.out.println("学生B收集了往年试题和参考资料。");
    }

    @Override
    protected void readInstructions() {
        System.out.println("学生B快速浏览了试题说明。");
    }

    @Override
    protected void writeAnswers() {
        System.out.println("学生B根据记忆快速写下了答案。");
    }

    @Override
    protected void review() {
        System.out.println("学生B简要复习了答案。");
    }
}

1.1.2 存在问题

  • 基类中定义的普通方法,每个方法不强制要求子类必须覆盖,一方面:子类可能遗漏某个实现步骤;另一方面:不同子类可能实现不同的行为,可能导致子类之间的不一致

  • 重复代码:多个子类中有重复的代码逻辑,增加了代码的重复性和维护难度。

  • 不符合开闭原则:如果要修改步骤的执行顺序或者添加新的步骤,需要修改所有子类中的实现,会导致重复代码和难以维护的情况。

1.2 方案二:采用模板方法模式实现

1.2.1 实现代码

  • 定义基类,基类定义了抄题的模板方法,包含一个完整的抄题流程。
abstract class TestPaper {
    // 模板方法
    public final void writeTestPaper() {
        // 收集试题材料
        gatherMaterials();
        // 阅读试题说明
        readInstructions();
        // 写答案
        writeAnswers();  
        // 复习检查
        review();             
    }

    // 抽象方法,让子类实现
    protected abstract void gatherMaterials();
    protected abstract void readInstructions();
    protected abstract void writeAnswers();
    protected abstract void review();
}
  • 实现学生A具体子类,实现抄题的具体步骤。
class StudentA extends TestPaper {
    @Override
    protected void gatherMaterials() {
        System.out.println("学生A收集了教科书和笔记。");
    }

    @Override
    protected void readInstructions() {
        System.out.println("学生A仔细阅读了试题说明。");
    }

    @Override
    protected void writeAnswers() {
        System.out.println("学生A根据笔记写下了答案。");
    }

    @Override
    protected void review() {
        System.out.println("学生A复习检查了答案。");
    }
}

  • 实现学生B的具体子类,可以有不同的实现。
class StudentB extends TestPaper {
    @Override
    protected void gatherMaterials() {
        System.out.println("学生B收集了往年试题和参考资料。");
    }

    @Override
    protected void readInstructions() {
        System.out.println("学生B快速浏览了试题说明。");
    }

    @Override
    protected void writeAnswers() {
        System.out.println("学生B根据记忆快速写下了答案。");
    }

    @Override
    protected void review() {
        System.out.println("学生B简要复习了答案。");
    }
}

image.png

1.2.2 上述方案优点

将步骤的控制逻辑集中在基类中,从而提供一致的执行流程减少重复代码简化维护,并确保子类按照统一的步骤执行任务。

2. 模板方法模式

2.1 定义

模板方法模式(Template Method Pattern):行为设计模式,在超类中定义了一个算法的框架,允许子类在不结构的情况下重写算法的特定步骤。

image.png

2.2 结构

image.png

2.3 适用场景

  • 只希望客户端扩展某个特定算法步骤,而不是整个算法或其结构时;

  • 多个类的算法除一些细微不同之外几乎完全一样时。

2.4 优缺点

2.4.1 优点

  • 封装不变部分,扩展可变部分;

  • 提取公共部分代码,便于维护;

  • 符合开闭原则:行为由父类控制,子类实现,因此子类可以通过扩展的方式增加相应功能。

2.4.2 缺点

  • 模板方法中的步骤越多,其维护工作就可能会越困难。

参考资料