设计模式详解-模板方法模式
设计模式详解:模板方法模式
一、模式概述
模板方法模式(Template Method Pattern)是行为型设计模式中最具框架设计价值的模式,其核心意图在于定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的前提下,重新定义算法的某些特定步骤,从而实现代码复用与扩展的优雅平衡。
模板方法模式的命名直接揭示了其本质——"模板"即固定的框架、可复用的范式。在出版领域,书籍模板规定了封面、目录、正文、附录的结构,作者只需填充具体内容;在软件开发中,JUnit的setUp-test-tearDown、Spring的init-service-destroy、Servlet的doGet/doPost,均是模板方法模式的经典体现。这些框架定义了生命周期或处理流程的骨架,开发者通过覆盖特定钩子方法,插入自定义逻辑,而无需关心整体流程的控制。
模板方法模式的深层价值在于**“好莱坞原则”(Don’t call us, we’ll call you)**的实现。父类作为高层组件控制流程,子类作为低层组件提供具体实现,但调用方向反转——不是子类调用父类,而是父类调用子类。这一原则解耦了高层算法与低层实现,使得框架代码能够稳定复用,而扩展点保持灵活开放。
二、模式结构
模板方法模式包含两个核心角色,形成清晰的层次协作:
抽象类(AbstractClass):定义抽象的原语操作(primitive operations),具体的子类将重定义这些操作以实现一个算法的各步骤;实现一个模板方法,定义算法的骨架,该模板方法不仅调用原语操作,也调用定义在AbstractClass中的其他操作。
具体类(ConcreteClass):实现原语操作以完成算法中与特定子类相关的步骤。
模板方法模式的关键机制包括:
- 模板方法(Template Method):声明为final,防止子类篡改算法骨架。
- 原语操作(Primitive Operations):抽象或保护的方法,子类必须或可选覆盖。
- 钩子方法(Hook Methods):提供默认空实现,子类可选择性覆盖以插入扩展点。
- 具体方法(Concrete Methods):在抽象类中完整实现,子类直接继承使用。
三、深度案例:企业级数据处理管道引擎
以下展示一个真实场景下的模板方法模式应用——金融数据平台的批量数据处理引擎,支持ETL、风控计算、报表生成等多种处理场景,每种场景共享相同的处理骨架,但具体实现各异。
3.1 抽象模板:数据处理管道
java
/**
* 抽象类:数据处理管道模板
* 定义所有数据处理场景的通用骨架
*/
public abstract class DataProcessingPipeline<T extends ProcessingContext, R extends ProcessingResult> {
// 依赖注入的基础设施
protected final PipelineMetrics metrics;
protected final EventPublisher eventPublisher;
protected final ErrorHandler errorHandler;
protected final CheckpointManager checkpointManager;
protected DataProcessingPipeline(PipelineMetrics metrics,
EventPublisher eventPublisher,
ErrorHandler errorHandler,
CheckpointManager checkpointManager) {
this.metrics = metrics;
this.eventPublisher = eventPublisher;
this.errorHandler = errorHandler;
this.checkpointManager = checkpointManager;
}
/**
* 模板方法:定义数据处理的核心骨架
* 声明为final,防止子类篡改流程
*/
public final R execute(T context) {
String pipelineId = generatePipelineId();
context.setPipelineId(pipelineId);
long startTime = System.currentTimeMillis();
try {
// 1. 前置校验(钩子,可选覆盖)
if (!preValidate(context)) {
return createValidationFailureResult(context);
}
// 2. 资源准备(抽象方法,必须实现)
ProcessingResource resource = prepareResource(context);
// 3. 数据加载(抽象方法,必须实现)
DataSource<T> dataSource = loadData(context);
// 4. 处理前钩子(钩子,可选覆盖)
beforeProcess(context, dataSource);
// 5. 核心处理循环(具体方法,通用实现)
ProcessSummary summary = processLoop(context, dataSource, resource);
// 6. 处理后钩子(钩子,可选覆盖)
afterProcess(context, summary);
// 7. 结果持久化(抽象方法,必须实现)
R result = persistResult(context, summary);
// 8. 清理资源(具体方法,通用实现,可覆盖)
cleanup(context, resource);
// 9. 发布成功事件
publishSuccessEvent(context, result, System.currentTimeMillis() - startTime);
metrics.recordSuccess(getPipelineType(), System.currentTimeMillis() - startTime);
return result;
} catch (PipelineRetryableException e) {
// 可重试异常:记录检查点,抛出由调度器重试
checkpointManager.saveCheckpoint(pipelineId, context, e.getProgress());
metrics.recordRetryable(getPipelineType(), e);
throw e;
} catch (PipelineFatalException e) {
// 致命异常:记录失败,发送告警
errorHandler.handleFatal(e, context);
metrics.recordFatal(getPipelineType(), e);
return createFatalFailureResult(context, e);
} catch (Exception e) {
// 未预期异常:转换后处理
PipelineFatalException wrapped = new PipelineFatalException(
"未预期的处理异常", e, context);
return handleUnexpectedException(context, wrapped);
}
}
// ========== 抽象方法:子类必须实现 ==========
/**
* 获取管道类型标识
*/
protected abstract String getPipelineType();
/**
* 准备处理资源(连接池、计算引擎实例、临时存储等)
*/
protected abstract ProcessingResource prepareResource(T context);
/**
* 加载数据源
*/
protected abstract DataSource<T> loadData(T context);
/**
* 单条数据处理(在processLoop中被调用)
*/
protected abstract ProcessedRecord processRecord(T context, RawRecord record,
ProcessingResource resource);
/**
* 持久化处理结果
*/
protected abstract R persistResult(T context, ProcessSummary summary);
// ========== 钩子方法:子类可选覆盖 ==========
/**
* 前置校验钩子
*/
protected boolean preValidate(T context) {
return true; // 默认通过
}
/**
* 处理前钩子
*/
protected void beforeProcess(T context, DataSource<T> dataSource) {
// 默认空实现
}
/**
* 处理后钩子
*/
protected void afterProcess(T context, ProcessSummary summary) {
// 默认空实现
}
/**
* 异常处理钩子
*/
protected R handleUnexpectedException(T context, PipelineFatalException e) {
errorHandler.handleFatal(e, context);
return createFatalFailureResult(context, e);
}
// ========== 具体方法:通用实现,子类可直接继承 ==========
/**
* 核心处理循环:通用的分批、并发、容错逻辑
*/
protected ProcessSummary processLoop(T context, DataSource<T> dataSource,
ProcessingResource resource) {
ProcessSummary summary = new ProcessSummary();
List<ProcessedRecord> batch = new ArrayList<>(context.getBatchSize());
try (RecordStream stream = dataSource.openStream()) {
while (stream.hasNext()) {
RawRecord record = stream.next();
summary.incrementTotal();
try {
// 单条处理(调用子类实现)
ProcessedRecord processed = processRecord(context, record, resource);
batch.add(processed);
summary.incrementSuccess();
} catch (RecordSkippableException e) {
// 可跳过异常:记录日志,继续
summary.addSkipped(record.getId(), e.getReason());
metrics.recordSkipped(getPipelineType());
} catch (RecordRetryableException e) {
// 单条重试
ProcessedRecord processed = retryRecord(context, record, resource, e);
if (processed != null) {
batch.add(processed);
summary.incrementSuccess();
} else {
summary.addFailed(record.getId(), "重试耗尽");
}
} catch (Exception e) {
// 单条失败,根据策略决定
if (context.isFailFast()) {
throw new PipelineFatalException("快速失败模式:单条处理异常", e);
}
summary.addFailed(record.getId(), e.getMessage());
metrics.recordSingleFailure(getPipelineType());
}
// 批量提交
if (batch.size() >= context.getBatchSize()) {
flushBatch(batch, resource);
checkpointManager.updateProgress(context.getPipelineId(),
stream.getProgress());
batch.clear();
}
}
// 剩余数据提交
if (!batch.isEmpty()) {
flushBatch(batch, resource);
}
}
return summary;
}
/**
* 单条重试逻辑
*/
protected ProcessedRecord retryRecord(T context, RawRecord record,
ProcessingResource resource,
RecordRetryableException originalError) {
for (int attempt = 1; attempt <= context.getMaxRetries(); attempt++) {
try {
Thread.sleep(context.getRetryIntervalMs() * attempt); // 指数退避
return processRecord(context, record, resource);
} catch (RecordRetryableException e) {
// 继续重试
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
}
return null;
}
/**
* 批量刷新
*/
protected void flushBatch(List<ProcessedRecord> batch, ProcessingResource resource) {
resource.getBatchWriter().write(batch);
}
/**
* 资源清理
*/
protected void cleanup(T context, ProcessingResource resource) {
if (resource != null) {
resource.close();
}
}
/**
* 生成管道ID
*/
protected String generatePipelineId() {
return getPipelineType() + "-" + UUID.randomUUID().toString().substring(0, 8);
}
/**
* 发布成功事件
*/
protected void publishSuccessEvent(T context, R result, long durationMs) {
eventPublisher.publish(PipelineCompletedEvent.builder()
.pipelineId(context.getPipelineId())
.pipelineType(getPipelineType())
.durationMs(durationMs)
.recordCount(result.getProcessedCount())
.build());
}
// 结果工厂方法
protected abstract R createValidationFailureResult(T context);
protected abstract R createFatalFailureResult(T context, PipelineFatalException e);
}
3.2 具体类:ETL数据清洗管道
java
/**
* 具体类:ETL数据清洗管道
* 实现数据抽取、转换、加载的完整流程
*/
@Component
public class EtlCleaningPipeline extends DataProcessingPipeline<EtlContext, EtlResult> {
private final DataSourceManager dataSourceManager;
private final TransformationEngine transformationEngine;
private final DataQualityEngine qualityEngine;
private final TargetRepository targetRepository;
@Autowired
public EtlCleaningPipeline(PipelineMetrics metrics,
EventPublisher eventPublisher,
ErrorHandler errorHandler,
CheckpointManager checkpointManager,
DataSourceManager dataSourceManager,
TransformationEngine transformationEngine,
DataQualityEngine qualityEngine,
TargetRepository targetRepository) {
super(metrics, eventPublisher, errorHandler, checkpointManager);
this.dataSourceManager = dataSourceManager;
this.transformationEngine = transformationEngine;
this.qualityEngine = qualityEngine;
this.targetRepository = targetRepository;
}
@Override
protected String getPipelineType() {
return "ETL-CLEANING";
}
@Override
protected boolean preValidate(EtlContext context) {
// ETL特定校验:源表存在性、字段映射完整性
if (!dataSourceManager.validateSource(context.getSourceConfig())) {
return false;
}
if (context.getFieldMappings().isEmpty()) {
return false;
}
return super.preValidate(context);
}
@Override
protected ProcessingResource prepareResource(EtlContext context) {
// 准备Spark会话或JDBC连接池
EtlResource resource = new EtlResource();
resource.setSparkSession(SparkSession.builder()
.appName("ETL-" + context.getPipelineId())
.config("spark.sql.adaptive.enabled", "true")
.getOrCreate());
resource.setJdbcTemplate(dataSourceManager.getJdbcTemplate(
context.getTargetConfig()));
return resource;
}
@Override
protected DataSource<EtlContext> loadData(EtlContext context) {
// 根据配置选择加载方式:全量、增量、CDC
switch (context.getLoadStrategy()) {
case FULL:
return new FullTableDataSource(context.getSourceConfig());
case INCREMENTAL:
return new IncrementalDataSource(context.getSourceConfig(),
context.getLastWatermark());
case CDC:
return new CdcDataSource(context.getSourceConfig(),
context.getBinlogPosition());
default:
throw new UnsupportedOperationException("未知的加载策略");
}
}
@Override
protected ProcessedRecord processRecord(EtlContext context, RawRecord record,
ProcessingResource resource) {
EtlResource etlResource = (EtlResource) resource;
// 1. 数据抽取(已解序列化)
Map<String, Object> rawData = record.getData();
// 2. 字段映射与转换
Map<String, Object> transformed = transformationEngine.transform(
rawData,
context.getFieldMappings(),
context.getTransformationRules()
);
// 3. 数据质量检查
QualityReport quality = qualityEngine.inspect(transformed,
context.getQualityRules());
if (!quality.isPassed()) {
// 质量不达标:根据策略处理
if (context.getQualityStrategy() == QualityStrategy.STRICT) {
throw new RecordSkippableException("质量检查未通过: " +
quality.getFailedChecks());
}
// 宽松模式:标记质量等级,继续处理
transformed.put("_quality_score", quality.getScore());
}
// 4. 标准化处理
transformed.put("_etl_timestamp", Instant.now());
transformed.put("_pipeline_id", context.getPipelineId());
return new ProcessedRecord(record.getId(), transformed);
}
@Override
protected void beforeProcess(EtlContext context, DataSource<EtlContext> dataSource) {
// ETL特定:创建目标表(如不存在)
targetRepository.ensureTableExists(context.getTargetTable(),
context.getFieldMappings());
// 注册UDF
EtlResource resource = (EtlResource) context.getResource();
resource.getSparkSession().udf().register("customHash",
new CustomHashUdf(), DataTypes.StringType);
}
@Override
protected EtlResult persistResult(EtlContext context, ProcessSummary summary) {
// 更新水印
if (context.getLoadStrategy() == LoadStrategy.INCREMENTAL) {
targetRepository.updateWatermark(context.getTargetTable(),
context.getCurrentWatermark());
}
// 生成质量报告
QualityReport aggregateQuality = qualityEngine.aggregate(
context.getPipelineId());
return EtlResult.builder()
.pipelineId(context.getPipelineId())
.processedCount(summary.getTotal())
.successCount(summary.getSuccess())
.skippedCount(summary.getSkipped().size())
.failedCount(summary.getFailed().size())
.qualityReport(aggregateQuality)
.targetTable(context.getTargetTable())
.build();
}
@Override
protected EtlResult createValidationFailureResult(EtlContext context) {
return EtlResult.builder()
.pipelineId(context.getPipelineId())
.status(EtlStatus.VALIDATION_FAILED)
.build();
}
@Override
protected EtlResult createFatalFailureResult(EtlContext context,
PipelineFatalException e) {
return EtlResult.builder()
.pipelineId(context.getPipelineId())
.status(EtlStatus.FAILED)
.errorMessage(e.getMessage())
.build();
}
}
3.3 具体类:风控特征计算管道
java
/**
* 具体类:风控特征计算管道
* 实时计算用户/交易的风险特征
*/
@Component
public class RiskFeaturePipeline extends DataProcessingPipeline<RiskContext, RiskResult> {
private final FeatureRepository featureRepository;
private final FeatureCalculatorRegistry calculatorRegistry;
private final RuleEngine ruleEngine;
private final RedisTemplate<String, String> redisTemplate;
@Autowired
public RiskFeaturePipeline(PipelineMetrics metrics,
EventPublisher eventPublisher,
ErrorHandler errorHandler,
CheckpointManager checkpointManager,
FeatureRepository featureRepository,
FeatureCalculatorRegistry calculatorRegistry,
RuleEngine ruleEngine,
RedisTemplate<String, String> redisTemplate) {
super(metrics, eventPublisher, errorHandler, checkpointManager);
this.featureRepository = featureRepository;
this.calculatorRegistry = calculatorRegistry;
this.ruleEngine = ruleEngine;
this.redisTemplate = redisTemplate;
}
@Override
protected String getPipelineType() {
return "RISK-FEATURE";
}
@Override
protected ProcessingResource prepareResource(RiskContext context) {
// 预加载特征计算器
RiskResource resource = new RiskResource();
resource.setCalculators(context.getRequiredFeatures().stream()
.map(calculatorRegistry::get)
.collect(Collectors.toList()));
// 预加载用户历史数据到本地缓存
resource.setUserHistory(loadUserHistory(context.getUserId()));
return resource;
}
@Override
protected DataSource<RiskContext> loadData(RiskContext context) {
// 风控场景:输入是事件流而非数据表
return new EventStreamDataSource(context.getEventStream());
}
@Override
protected ProcessedRecord processRecord(RiskContext context, RawRecord record,
ProcessingResource resource) {
RiskEvent event = (RiskEvent) record.getData();
RiskResource riskResource = (RiskResource) resource;
// 1. 计算各项特征
Map<String, Object> features = new HashMap<>();
for (FeatureCalculator calculator : riskResource.getCalculators()) {
Object value = calculator.calculate(event, riskResource.getUserHistory());
features.put(calculator.getFeatureName(), value);
}
// 2. 特征交叉(组合特征)
Map<String, Object> crossedFeatures = generateCrossFeatures(features);
features.putAll(crossedFeatures);
// 3. 规则命中检查
List<RuleHit> ruleHits = ruleEngine.evaluate(features, context.getActiveRules());
// 4. 更新用户历史(内存中)
riskResource.getUserHistory().update(event, features);
// 5. 写入特征存储(实时 + 离线)
featureRepository.writeRealtime(context.getUserId(), event.getEventId(), features);
return new ProcessedRecord(event.getEventId(), Map.of(
"features", features,
"ruleHits", ruleHits,
"riskScore", aggregateRiskScore(ruleHits)
));
}
@Override
protected void afterProcess(RiskContext context, ProcessSummary summary) {
// 批量写入离线特征库(T+1使用)
featureRepository.flushOfflineBatch(context.getUserId());
// 更新Redis中的实时特征缓存
redisTemplate.opsForHash().putAll(
"risk:features:" + context.getUserId(),
serializeFeatures(summary.getLastBatch())
);
}
@Override
protected RiskResult persistResult(RiskContext context, ProcessSummary summary) {
return RiskResult.builder()
.pipelineId(context.getPipelineId())
.userId(context.getUserId())
.processedEvents(summary.getTotal())
.featureSnapshot(featureRepository.getLatestSnapshot(context.getUserId()))
.build();
}
@Override
protected RiskResult createValidationFailureResult(RiskContext context) {
return RiskResult.builder()
.pipelineId(context.getPipelineId())
.status(RiskStatus.INVALID_INPUT)
.build();
}
@Override
protected RiskResult createFatalFailureResult(RiskContext context,
PipelineFatalException e) {
// 风控失败:降级返回基础特征
return RiskResult.builder()
.pipelineId(context.getPipelineId())
.status(RiskStatus.DEGRADED)
.fallbackFeatures(loadFallbackFeatures(context.getUserId()))
.build();
}
private Map<String, Object> generateCrossFeatures(Map<String, Object> baseFeatures) {
// 特征交叉逻辑:如 device_fingerprint × ip_risk_score
Map<String, Object> crossed = new HashMap<>();
// 实现省略...
return crossed;
}
private double aggregateRiskScore(List<RuleHit> hits) {
return hits.stream()
.mapToDouble(RuleHit::getScore)
.max()
.orElse(0.0);
}
}
3.4 具体类:监管报表生成管道
java
/**
* 具体类:监管报表生成管道
* 满足央行、银保监会的报送要求
*/
@Component
public class RegulatoryReportPipeline extends
DataProcessingPipeline<ReportContext, ReportResult> {
private final ReportTemplateRepository templateRepository;
private final DataExtractor dataExtractor;
private final ReportValidator reportValidator;
private final RegulatoryGateway regulatoryGateway;
@Override
protected String getPipelineType() {
return "REGULATORY-REPORT";
}
@Override
protected boolean preValidate(ReportContext context) {
// 校验报送日期是否为工作日
if (!isWorkingDay(context.getReportDate())) {
return false;
}
// 校验模板版本是否为最新
ReportTemplate template = templateRepository.getTemplate(
context.getReportType(), context.getReportDate());
if (template.getVersion() != context.getExpectedTemplateVersion()) {
return false;
}
return super.preValidate(context);
}
@Override
protected DataSource<ReportContext> loadData(ReportContext context) {
// 监管报表:从数据仓库按主题域抽取
return new WarehouseDataSource(context.getReportType().getRequiredTables(),
context.getReportDate());
}
@Override
protected ProcessedRecord processRecord(ReportContext context, RawRecord record,
ProcessingResource resource) {
// 数据映射到监管标准编码
Map<String, Object> regulatoryData = mapToRegulatoryFormat(
record.getData(), context.getReportType());
// 金额单位转换(元 -> 万元)
convertMonetaryUnit(regulatoryData, context.getReportType().getMonetaryUnit());
// 脱敏处理
desensitizeSensitiveFields(regulatoryData, context.getReportType());
return new ProcessedRecord(record.getId(), regulatoryData);
}
@Override
protected void afterProcess(ReportContext context, ProcessSummary summary) {
// 报表特定:汇总校验
ReportTemplate template = templateRepository.getTemplate(
context.getReportType(), context.getReportDate());
List<ValidationRule> balanceRules = template.getBalanceRules();
for (ValidationRule rule : balanceRules) {
if (!validateBalance(summary.getAggregatedData(), rule)) {
throw new PipelineFatalException("报表平衡校验未通过: " + rule.getName());
}
}
}
@Override
protected ReportResult persistResult(ReportContext context, ProcessSummary summary) {
// 生成监管标准格式文件
byte[] reportFile = generateRegulatoryFile(context.getReportType(),
summary.getProcessedRecords());
// 数字签名
byte[] signature = signReport(reportFile);
// 报送
SubmitResult submitResult = regulatoryGateway.submit(
context.getReportType(),
context.getReportDate(),
reportFile,
signature
);
return ReportResult.builder()
.pipelineId(context.getPipelineId())
.reportType(context.getReportType())
.reportDate(context.getReportDate())
.submitReference(submitResult.getReference())
.status(submitResult.isAccepted() ? ReportStatus.SUBMITTED :
ReportStatus.REJECTED)
.build();
}
// 监管特定方法...
}
四、模板方法模式的高级主题
4.1 回调接口替代继承
Java 8+中,函数式接口可替代抽象类,提供更灵活的扩展机制:
java
/**
* 基于回调的模板方法
* 避免继承,支持运行时组合
*/
public class CallbackBasedPipeline<T, R> {
private final Function<T, Boolean> preValidator;
private final Function<T, DataSource<T>> dataLoader;
private final BiFunction<T, RawRecord, ProcessedRecord> processor;
private final BiFunction<T, ProcessSummary, R> resultPersister;
public CallbackBasedPipeline(Builder<T, R> builder) {
this.preValidator = builder.preValidator;
this.dataLoader = builder.dataLoader;
this.processor = builder.processor;
this.resultPersister = builder.resultPersister;
}
public R execute(T context) {
if (!preValidator.apply(context)) {
throw new ValidationException();
}
DataSource<T> dataSource = dataLoader.apply(context);
ProcessSummary summary = processLoop(context, dataSource);
return resultPersister.apply(context, summary);
}
// Builder模式...
}
// 使用Lambda配置管道
CallbackBasedPipeline<EtlContext, EtlResult> pipeline =
new CallbackBasedPipeline.Builder<EtlContext, EtlResult>()
.preValidator(ctx -> ctx.getFieldMappings() != null)
.dataLoader(ctx -> new FullTableDataSource(ctx.getSourceConfig()))
.processor((ctx, record) -> {
// 自定义处理逻辑
return new ProcessedRecord(record.getId(), transformed);
})
.resultPersister((ctx, summary) -> {
// 自定义持久化
return EtlResult.builder().build();
})
.build();
4.2 模板方法与Spring的整合
java
/**
* Spring风格的模板方法:通过Bean注入扩展点
*/
public abstract class SpringAwarePipeline<T extends ProcessingContext,
R extends ProcessingResult>
implements ApplicationContextAware {
private ApplicationContext applicationContext;
// 注入扩展点而非强制覆盖
@Autowired(required = false)
private List<PipelineInterceptor<T, R>> interceptors = new ArrayList<>();
@Override
public void setApplicationContext(ApplicationContext context) {
this.applicationContext = context;
}
public final R execute(T context) {
// 执行拦截器链
PipelineChain<T, R> chain = new DefaultPipelineChain<>(this, interceptors);
return chain.proceed(context);
}
// 抽象方法定义扩展点...
}
五、模板方法与相关模式的辨析
模板方法 vs 策略模式:模板方法定义算法骨架,子类覆盖步骤;策略模式封装 interchangeable 的算法族,客户端选择策略。模板方法是"白盒复用"(知道骨架),策略是"黑盒复用"(仅知道接口)。
模板方法 vs 工厂方法:工厂方法是模板方法的特殊形式,模板方法中的某一步骤专门用于创建对象。二者常结合使用:模板方法定义流程,工厂方法创建流程中需要的对象。
模板方法 vs 建造者模式:建造者逐步构建复杂对象,模板方法定义操作算法。二者可结合:模板方法的某步骤使用建造者创建对象。
六、设计陷阱与规避策略
陷阱一:模板方法过于庞大
当模板方法包含数十个步骤时,难以理解和维护。解决方案:提取子模板方法,或将部分步骤委托给策略对象。
陷阱二:子类泛滥
每个变体都需要新子类,导致类爆炸。解决方案:结合策略模式,将部分变化点提取为可配置的策略对象。
陷阱三:钩子方法滥用
过多钩子使子类难以决定覆盖哪些。解决方案:明确区分必须实现的抽象方法与可选的钩子,文档化钩子的使用场景。
七、结语
模板方法模式是框架设计的基石,它将流程控制与具体实现解耦,使高层算法稳定复用,低层扩展灵活开放。在数据管道、生命周期管理、工作流引擎等场景中,模板方法模式是不可或缺的组织原则。理解其"好莱坞原则"的深层哲学,掌握抽象方法、钩子方法、具体方法的协同设计,警惕继承僵化与模板膨胀的陷阱,是构建可扩展框架的核心能力。模板方法模式不仅是一种代码结构,更是一种设计思维——定义不变的骨架,拥抱变化的细节。