意图
模板方法模式(Template Method)是一种行为设计模式, 它在超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。
解决方案
模板方法模式建议将算法分解为一系列步骤, 然后将这些步骤改写为方法, 最后在 “模板方法” 中依次调用这些方法。 步骤可以是 抽象
的, 也可以有一些默认的实现。 为了能够使用算法, 客户端需要自行提供子类并实现所有的抽象步骤。 如有必要还需重写一些步骤 (但这一步中不包括模板方法自身)。
模板方法模式结构
抽象类 (AbstractClass) 会声明作为算法步骤的方法, 以及依次调用它们的实际模板方法。 算法步骤可以被声明为 抽象
类型, 也可以提供一些默认实现。
具体类 (ConcreteClass) 可以重写所有步骤, 但不能重写模板方法自身。
模板方法模式适合应用场景
- 当你只希望客户端扩展某个特定算法步骤, 而不是整个算法或其结构时, 可使用模板方法模式。
模板方法将整个算法转换为一系列独立的步骤, 以便子类能对其进行扩展, 同时还可让超类中所定义的结构保持完整。
- 当多个类的算法除一些细微不同之外几乎完全一样时, 你可使用该模式。 但其后果就是, 只要算法发生变化, 你就可能需要修改所有的类。
在将算法转换为模板方法时, 你可将相似的实现步骤提取到超类中以去除重复代码。 子类间各不同的代码可继续保留在子类中。
模板方法模式优缺点
优点:
- 你可仅允许客户端重写一个大型算法中的特定部分, 使得算法其他部分修改对其所造成的影响减小。
- 你可将重复代码提取到一个超类中。
缺点:
- 部分客户端可能会受到算法框架的限制。
- 通过子类抑制默认步骤实现可能会导致违反_里氏替换原则_。
- 模板方法中的步骤越多, 其维护工作就可能会越困难。
与其他模式的关系
-
模板方法基于继承机制: 它允许你通过扩展子类中的部分内容来改变部分算法。 策略模式基于组合机制: 你可以通过对相应行为提供不同的策略来改变对象的部分行为。 模板方法在类层次上运作, 因此它是静态的。 策略在对象层次上运作, 因此允许在运行时切换行为。
在 Java 中使用模式
使用示例: 模版方法模式在 Java 框架中很常见。 开发者通常使用它来向框架用户提供通过继承实现的、 对标准功能进行扩展的简单方式。
这里是一些核心 Java 程序库中模版方法的示例:
-
java.io.InputStream
、java.io.OutputStream
、java.io.Reader
和java.io.Writer
的所有非抽象方法。 -
java.util.AbstractList
、java.util.AbstractSet
和java.util.AbstractMap
的所有非抽象方法。 -
javax.servlet.http.HttpServlet
, 所有默认发送 HTTP 405 “方法不允许” 错误响应的doXXX()
方法。 你可随时对其进行重写。
示例代码
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
public final void play() {
initialize();
startPlay();
endPlay();
}
}
public class Cricket extends Game {
@Override
void endPlay() {
System.out.println("Cricket Game Finished!");
}
@Override
void initialize() {
System.out.println("Cricket Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Cricket Game Started. Enjoy the game!");
}
}
public class Football extends Game {
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
}
public class Client {
public static void main(String[] args) {
Game game = new Cricket();
game.play();
System.out.println();
game = new Football();
game.play();
}
}