Java 中的模板方法模式:使用预定义脚手架简化复杂算法
大约 4 分钟
模板方法设计模式的意图
在操作中定义算法的骨架,将一些步骤延迟到子类。模板方法允许子类重新定义算法的某些步骤,而不会改变算法的结构。
带现实世界示例的模板方法模式的详细解释
现实世界的例子
现实世界中,模板方法模式的类比可以是制作一杯茶或咖啡。整个过程(算法)是相同的:烧水、冲泡饮料、倒入杯子、添加调味品。然而,冲泡饮料的具体步骤不同。对于茶,你需要将茶叶浸泡在热水中,而对于咖啡,你需要冲泡研磨过的咖啡豆。模板方法模式将过程的不变步骤(烧水、倒入、添加调味品)封装在基类中,同时允许子类定义特定的冲泡步骤,从而确保制作热饮的整体结构保持一致,同时允许在需要的地方进行定制。
通俗地说
Java 模板方法模式概述了父类中的核心步骤,允许子类定制详细的实现,从而增强 Java 编程中的代码可重用性和设计灵活性。
维基百科说
在面向对象编程中,模板方法是 Gamma 等人 在《设计模式》一书中识别出的行为设计模式之一。模板方法是超类(通常是抽象超类)中的一个方法,它根据若干高级步骤定义操作的骨架。这些步骤本身由与模板方法位于同一类中的附加辅助方法实现。
Java 中模板方法模式的编程示例
我们的编程示例是关于小偷和偷窃的。偷窃物品的步骤通常是一样的。首先,你选择目标,然后你以某种方式迷惑他,最后,你偷走物品。然而,实现这些步骤的方法有很多。
首先,我们介绍模板方法类 StealingMethod
及其具体的实现 SubtleMethod
和 HitAndRunMethod
。为了确保子类不会覆盖模板方法,模板方法(在本例中为方法 steal
)应声明为 final
,否则基类中定义的骨架可能会在子类中被覆盖。
@Slf4j
public abstract class StealingMethod {
protected abstract String pickTarget();
protected abstract void confuseTarget(String target);
protected abstract void stealTheItem(String target);
public final void steal() {
var target = pickTarget();
LOGGER.info("The target has been chosen as {}.", target);
confuseTarget(target);
stealTheItem(target);
}
}
@Slf4j
public class SubtleMethod extends StealingMethod {
@Override
protected String pickTarget() {
return "shop keeper";
}
@Override
protected void confuseTarget(String target) {
LOGGER.info("Approach the {} with tears running and hug him!", target);
}
@Override
protected void stealTheItem(String target) {
LOGGER.info("While in close contact grab the {}'s wallet.", target);
}
}
@Slf4j
public class HitAndRunMethod extends StealingMethod {
@Override
protected String pickTarget() {
return "old goblin woman";
}
@Override
protected void confuseTarget(String target) {
LOGGER.info("Approach the {} from behind.", target);
}
@Override
protected void stealTheItem(String target) {
LOGGER.info("Grab the handbag and run away fast!");
}
}
以下是包含模板方法的半兽人小偷类。
public class HalflingThief {
private StealingMethod method;
public HalflingThief(StealingMethod method) {
this.method = method;
}
public void steal() {
method.steal();
}
public void changeMethod(StealingMethod method) {
this.method = method;
}
}
最后,我们展示了半兽人小偷如何使用不同的偷窃方法。
public static void main(String[] args) {
var thief = new HalflingThief(new HitAndRunMethod());
thief.steal();
thief.changeMethod(new SubtleMethod());
thief.steal();
}
程序输出
11:06:01.721 [main] INFO com.iluwatar.templatemethod.StealingMethod -- The target has been chosen as old goblin woman.
11:06:01.723 [main] INFO com.iluwatar.templatemethod.HitAndRunMethod -- Approach the old goblin woman from behind.
11:06:01.723 [main] INFO com.iluwatar.templatemethod.HitAndRunMethod -- Grab the handbag and run away fast!
11:06:01.723 [main] INFO com.iluwatar.templatemethod.StealingMethod -- The target has been chosen as shop keeper.
11:06:01.723 [main] INFO com.iluwatar.templatemethod.SubtleMethod -- Approach the shop keeper with tears running and hug him!
11:06:01.723 [main] INFO com.iluwatar.templatemethod.SubtleMethod -- While in close contact grab the shop keeper's wallet.
何时在 Java 中使用模板方法模式
模板方法模式应该在以下情况下使用
- 为了只实现算法的不变部分,并将可变的行为留给子类来实现
- 当子类之间应有的通用行为应该被提取并定位在公共类中,以避免代码重复。这是一个 "重构以泛化" 的好例子,正如 Opdyke 和 Johnson 所描述的那样。首先,你识别出现有代码中的差异,然后将差异分离到新的操作中。最后,你用一个调用其中一个新操作的模板方法替换不同的代码。
- 为了控制子类的扩展。你可以定义一个模板方法,它在特定点调用 "钩子" 操作,从而只允许在这些点进行扩展。
模板方法模式 Java 教程
Java 中模板方法模式的现实世界应用
- Java 的 Collections Framework 中的
AbstractList
和AbstractSet
类使用模板方法模式来定义列表和集合操作的通用算法。 - 像 JUnit 这样的框架使用模板方法来定义测试用例中的设置和拆卸过程。
模板方法模式的优缺点
优点
- 通过在基类中定义算法的不变部分来促进代码重用。
- 通过将通用行为封装在一个地方来简化代码维护。
- 通过允许子类覆盖算法的特定步骤来增强灵活性。
权衡
- 可能会导致类数量增加,使系统更加复杂。
- 需要仔细设计,以确保暴露给子类的步骤是有用且有意义的。
相关的 Java 设计模式
- 工厂方法:通常与模板方法一起使用,以创建算法特定步骤所需的對象。
- 策略:模板方法定义了算法的骨架,并允许子类实现特定的步骤,而策略模式则定义了算法族,并使它们可以互换。
- 子类沙箱:通过确保子类可以安全地覆盖算法的特定步骤,而不会导致意外的副作用,来补充模板方法。