模版方法模式是一种行为型设计模式,它定义了一个操作中的算法骨架,将某些步骤延迟到子类中实现。这种模式允许子类在不改变算法结构的情况下重新定义算法的某些特定步骤。
我在实际项目中使用模版方法模式最多的场景是在处理具有固定流程但具体实现可能变化的业务逻辑时。比如支付流程、订单处理流程等,这些业务通常有明确的步骤顺序,但每个步骤的具体实现可能因支付方式或订单类型而异。
模版方法模式主要包含两个角色:
抽象类(AbstractClass):
具体子类(ConcreteClass):
java复制public abstract class AbstractClass {
// 模版方法,定义算法骨架
public final void templateMethod() {
primitiveOperation1();
primitiveOperation2();
concreteOperation();
hook();
}
// 抽象操作,由子类实现
protected abstract void primitiveOperation1();
protected abstract void primitiveOperation2();
// 具体操作,已有默认实现
protected void concreteOperation() {
// 实现代码
}
// 钩子方法,子类可以选择性覆盖
protected void hook() {}
}
假设我们正在开发一个电商平台的订单处理系统,不同类型的订单(普通订单、团购订单、预售订单等)有相似的处理流程,但某些步骤的实现不同。
java复制public abstract class OrderProcessor {
// 模版方法
public final void processOrder(Order order) {
validate(order);
calculatePrice(order);
applyDiscounts(order);
if (needInventoryCheck(order)) {
checkInventory(order);
}
confirmOrder(order);
notifyUser(order);
}
protected abstract void validate(Order order);
protected abstract void calculatePrice(Order order);
protected void applyDiscounts(Order order) {
// 默认实现:应用基础折扣
}
// 钩子方法
protected boolean needInventoryCheck(Order order) {
return true;
}
protected final void checkInventory(Order order) {
// 库存检查的通用实现
}
protected abstract void confirmOrder(Order order);
protected void notifyUser(Order order) {
// 默认通知实现
}
}
java复制public class GroupOrderProcessor extends OrderProcessor {
@Override
protected void validate(Order order) {
// 团购订单特有的验证逻辑
}
@Override
protected void calculatePrice(Order order) {
// 团购价格计算逻辑
}
@Override
protected boolean needInventoryCheck(Order order) {
// 团购订单不需要实时检查库存
return false;
}
@Override
protected void confirmOrder(Order order) {
// 团购订单确认逻辑
}
}
模版方法过于庞大:
过多抽象方法:
违反里氏替换原则:
方法调用开销:
对象创建开销:
模版方法:
策略模式:
工厂方法模式常作为模版方法模式的一个步骤出现。例如,在对象创建流程中,模版方法定义创建步骤,而工厂方法负责具体实例化。
钩子方法提供了灵活的扩展点,但过度使用会导致代码难以理解。我的经验是:
模版方法模式的测试需要注意:
java复制public class AbstractClassTest {
@Test
public void testTemplateMethod() {
AbstractClass instance = new AbstractClass() {
@Override
protected void primitiveOperation1() {
// 测试实现
}
@Override
protected void primitiveOperation2() {
// 测试实现
}
};
instance.templateMethod();
// 断言验证
}
}
良好的文档对模版方法模式特别重要:
在Java 8+中,接口也可以定义模版方法:
java复制public interface Processor {
default void process() {
init();
doProcess();
cleanup();
}
void init();
void doProcess();
default void cleanup() {
// 默认清理逻辑
}
}
可以使用函数参数提供可变部分:
java复制public class FunctionalTemplate {
public void execute(Runnable step1, Runnable step2) {
preProcess();
step1.run();
step2.run();
postProcess();
}
private void preProcess() { /* ... */ }
private void postProcess() { /* ... */ }
}
模版方法定义构建流程,建造者模式提供具体构建实现:
java复制public abstract class ReportBuilder {
public final Report buildReport() {
Report report = createReport();
buildHeader(report);
buildBody(report);
buildFooter(report);
return report;
}
protected abstract Report createReport();
protected abstract void buildHeader(Report report);
protected abstract void buildBody(Report report);
protected abstract void buildFooter(Report report);
}
模版方法定义处理流程,责任链中的每个处理器实现具体步骤:
java复制public abstract class Handler {
private Handler next;
public final void handle(Request request) {
if (canHandle(request)) {
process(request);
}
if (next != null) {
next.handle(request);
}
}
protected abstract boolean canHandle(Request request);
protected abstract void process(Request request);
public void setNext(Handler next) {
this.next = next;
}
}
测试生命周期就是典型的模版方法模式:
java复制public abstract class TestCase {
public void runBare() throws Throwable {
setUp();
try {
runTest();
} finally {
tearDown();
}
}
protected void setUp() throws Exception {}
protected void tearDown() throws Exception {}
protected void runTest() throws Throwable {
// 反射调用测试方法
}
}
当发现以下情况时,考虑使用模版方法模式:
假设我们有两个类处理相似流程:
java复制class OrderProcessorA {
void process() {
step1();
step2A(); // 特定实现
step3();
}
}
class OrderProcessorB {
void process() {
step1();
step2B(); // 特定实现
step3();
}
}
重构为模版方法模式:
java复制abstract class AbstractOrderProcessor {
final void process() {
step1();
step2();
step3();
}
private void step1() { /* ... */ }
abstract void step2();
private void step3() { /* ... */ }
}
class OrderProcessorA extends AbstractOrderProcessor {
void step2() { /* A实现 */ }
}
class OrderProcessorB extends AbstractOrderProcessor {
void step2() { /* B实现 */ }
}
当模版方法模式出现以下问题时,考虑其他模式:
对于计算密集型步骤,可以在抽象类中实现缓存:
java复制public abstract class CachedTemplate {
private final Map<String, Object> cache = new HashMap<>();
public final Object execute(String key) {
return cache.computeIfAbsent(key, k -> compute(k));
}
protected abstract Object compute(String key);
}
模版方法可以封装线程安全逻辑:
java复制public abstract class ConcurrentTemplate {
public final void execute() {
lock();
try {
doExecute();
} finally {
unlock();
}
}
protected abstract void doExecute();
private void lock() { /* ... */ }
private void unlock() { /* ... */ }
}
不使用继承,而是通过回调接口:
java复制public class Template {
public void execute(Callback callback) {
step1();
callback.execute();
step2();
}
public interface Callback {
void execute();
}
}
使用注解标记需要插入的方法:
java复制public class AnnotationTemplate {
public void process(Object processor) {
// 通过反射查找并调用标记的方法
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TemplateStep {
int order();
}
Python支持多重继承,可以更灵活地组合模版:
python复制class TemplateMixin:
def template_method(self):
self.step1()
self.step2()
def step1(self):
print("Default step1")
def step2(self):
raise NotImplementedError
class ConcreteClass(TemplateMixin, OtherMixin):
def step2(self):
print("Custom step2")
JavaScript使用原型继承,可以实现类似的模式:
javascript复制class AbstractClass {
templateMethod() {
this.primitiveOperation1();
this.primitiveOperation2();
}
primitiveOperation1() {
throw new Error("Must be implemented");
}
primitiveOperation2() {
throw new Error("Must be implemented");
}
}
class ConcreteClass extends AbstractClass {
primitiveOperation1() {
console.log("Operation1 implementation");
}
primitiveOperation2() {
console.log("Operation2 implementation");
}
}
游戏循环是典型的模版方法:
java复制public abstract class Game {
public final void run() {
init();
while (!isGameOver()) {
processInput();
update();
render();
}
cleanup();
}
protected abstract void init();
protected abstract void processInput();
protected abstract void update();
protected abstract void render();
protected abstract boolean isGameOver();
protected abstract void cleanup();
}
交易处理流程:
java复制public abstract class TradeProcessor {
public final void processTrade(Trade trade) {
validate(trade);
enrich(trade);
calculateRisk(trade);
if (needsApproval(trade)) {
approve(trade);
}
execute(trade);
settle(trade);
}
protected abstract void validate(Trade trade);
protected abstract void enrich(Trade trade);
protected abstract void calculateRisk(Trade trade);
protected boolean needsApproval(Trade trade) {
return trade.getAmount() > 1000000;
}
protected abstract void approve(Trade trade);
protected abstract void execute(Trade trade);
protected abstract void settle(Trade trade);
}
使用Mockito测试抽象类:
java复制public class AbstractClassTest {
@Test
public void testTemplateMethodFlow() {
AbstractClass instance = Mockito.mock(
AbstractClass.class,
Mockito.CALLS_REAL_METHODS
);
instance.templateMethod();
InOrder inOrder = Mockito.inOrder(instance);
inOrder.verify(instance).primitiveOperation1();
inOrder.verify(instance).primitiveOperation2();
}
}
重点测试覆盖的方法:
java复制public class ConcreteClassTest {
@Test
public void testOverriddenMethods() {
ConcreteClass instance = new ConcreteClass();
// 测试特定实现
instance.primitiveOperation1();
// 验证状态或行为
}
}
现代语言中,函数式特性提供了替代方案:
java复制public class FunctionalTemplate {
public void execute(
Runnable step1,
Runnable step2,
Runnable step3
) {
preProcess();
step1.run();
step2.run();
step3.run();
postProcess();
}
}
在微服务中,模版方法模式可以:
在实际项目中使用模版方法模式时,我总结了以下几点经验:
保持模版方法精简:模版方法应该只包含真正不变的流程步骤,过于复杂的模版方法会降低灵活性。
合理设计钩子方法:不是所有变化点都需要钩子,只为真正可能变化的点提供扩展。
文档至关重要:清晰说明模版方法的执行流程和各方法的预期行为,特别是那些需要子类实现的方法。
考虑组合替代继承:当变化点较多时,考虑结合策略模式,使用对象组合代替类继承。
测试覆盖模版流程:不仅要测试具体实现,还要确保模版方法的整体流程在各种情况下都正确执行。
命名要体现意图:模版方法和各步骤方法的命名应该清晰表达其作用和职责。
警惕过度设计:不是所有相似流程都需要模版方法,简单的复制粘贴有时比引入抽象更合适。
模版方法模式是我最常使用的设计模式之一,特别是在开发框架和基础组件时。它帮助我在保持架构稳定的同时,提供了必要的灵活性。但记住,没有银弹,要根据具体场景选择最合适的解决方案。