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简要复习了答案。");
}
}
1.2.2 上述方案优点
将步骤的控制逻辑集中在基类中,从而提供一致的执行流程、减少重复代码、简化维护,并确保子类按照统一的步骤执行任务。
2. 模板方法模式
2.1 定义
模板方法模式(Template Method Pattern):行为设计模式,在超类中定义了一个算法的框架,允许子类在不结构的情况下重写算法的特定步骤。
2.2 结构
2.3 适用场景
-
只希望客户端扩展某个特定算法步骤,而不是整个算法或其结构时;
-
多个类的算法除一些细微不同之外几乎完全一样时。
2.4 优缺点
2.4.1 优点
-
封装不变部分,扩展可变部分;
-
提取公共部分代码,便于维护;
-
符合开闭原则:行为由父类控制,子类实现,因此子类可以通过扩展的方式增加相应功能。
2.4.2 缺点
- 模板方法中的步骤越多,其维护工作就可能会越困难。
参考资料
- 《设计模式之禅》
- refactoringguru.cn/design-patt…