当前位置: 首页 > news >正文

设计模式-模板模式详解

设计模式-模板模式详解

1. 模板模式简介

1.1 定义

模板模式(Template Method Pattern)是一种行为型设计模式,它定义了一个操作中的算法骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

1.2 核心思想

  • 定义算法骨架:在抽象类中定义算法的基本流程
  • 延迟实现:将具体实现细节留给子类
  • 控制反转:父类控制算法流程,子类实现具体步骤

1.3 结构组成

AbstractClass (抽象类)
├── templateMethod() // 模板方法,定义算法骨架
├── primitiveOperation1() // 抽象方法,子类必须实现
├── primitiveOperation2() // 抽象方法,子类必须实现
└── hook() // 钩子方法,子类可选重写ConcreteClass (具体实现类)
├── primitiveOperation1() // 实现抽象方法
└── primitiveOperation2() // 实现抽象方法

2. 应用场景

2.1 适用场景

  1. 算法框架固定:当多个类有相同的算法结构,但具体实现不同时
  2. 代码复用:避免重复代码,提高代码复用性
  3. 扩展性好:需要在不修改现有代码的情况下扩展功能
  4. 控制流程:需要控制子类的扩展点

2.2 典型应用场景

  • 数据处理流程:数据读取 → 数据验证 → 数据处理 → 数据输出
  • 框架设计:Spring框架中的各种模板类
  • 测试框架:JUnit的测试生命周期
  • Web开发:MVC框架中的控制器模板
  • 数据库操作:JDBC模板、MyBatis的Executor

2.3 实际业务场景

// 示例:数据处理模板
public abstract class DataProcessor {// 模板方法public final void processData(String input) {String validatedData = validateData(input);String processedData = process(validatedData);saveData(processedData);notifyCompletion();}protected abstract String process(String data);protected String validateData(String data) {// 默认验证逻辑return data;}protected void saveData(String data) {// 默认保存逻辑}protected void notifyCompletion() {// 默认通知逻辑}
}

3. 重难点分析

3.1 设计重点

3.1.1 模板方法设计
  • final关键字:确保模板方法不被子类重写
  • 算法稳定性:核心算法流程应该稳定,避免频繁变更
  • 职责分离:抽象类负责流程控制,子类负责具体实现
3.1.2 抽象方法设计
  • 粒度适中:抽象方法粒度不能太细也不能太粗
  • 命名规范:使用清晰的命名表达方法职责
  • 参数设计:合理设计方法参数,避免过度复杂

3.2 实现难点

3.2.1 钩子方法设计
public abstract class AbstractProcessor {// 模板方法public final void process() {beforeProcess();doProcess();afterProcess();}// 钩子方法 - 子类可选重写protected void beforeProcess() {// 默认空实现}// 抽象方法 - 子类必须实现protected abstract void doProcess();// 钩子方法 - 子类可选重写protected void afterProcess() {// 默认空实现}
}
3.2.2 方法访问控制
  • protected:抽象方法使用protected,允许子类访问
  • final:模板方法使用final,防止被重写
  • private:辅助方法使用private,避免子类访问

3.3 常见问题

3.3.1 过度设计
  • 问题:为了使用模板模式而强行设计
  • 解决:评估是否真正需要模板模式
3.3.2 抽象层次不当
  • 问题:抽象层次过高或过低
  • 解决:根据实际业务需求确定合适的抽象层次
3.3.3 违反开闭原则
  • 问题:频繁修改抽象类
  • 解决:合理设计抽象方法,预留扩展点

4. 在Spring中的应用

4.1 Spring中的模板模式实现

4.1.1 JdbcTemplate
// Spring JdbcTemplate 模板模式实现
public class JdbcTemplate {public <T> T query(String sql, Object[] args, ResultSetExtractor<T> rse) {return execute(new PreparedStatementCreator() {public PreparedStatement createPreparedStatement(Connection con) {// 创建PreparedStatement}}, new PreparedStatementCallback<T>() {public T doInPreparedStatement(PreparedStatement ps) {// 执行查询并处理结果return rse.extractData(rs);}});}
}
4.1.2 RestTemplate
// Spring RestTemplate 模板模式实现
public class RestTemplate {public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType, Object... uriVariables) {// 模板方法:定义HTTP请求的通用流程return doExecute(url, method, requestEntity, responseType, uriVariables);}protected <T> ResponseEntity<T> doExecute(String url, HttpMethod method, HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType, Object... uriVariables) {// 具体实现}
}

4.2 Spring MVC中的模板模式

4.2.1 AbstractController
public abstract class AbstractController {public final ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {// 模板方法:定义请求处理的通用流程try {beforeHandle(request, response);ModelAndView result = handleRequestInternal(request, response);afterHandle(request, response, result);return result;} catch (Exception e) {return handleException(request, response, e);}}protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response);protected void beforeHandle(HttpServletRequest request, HttpServletResponse response) {// 钩子方法}protected void afterHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView modelAndView) {// 钩子方法}
}

4.3 Spring Boot中的模板模式

4.3.1 ApplicationRunner
public interface ApplicationRunner {void run(ApplicationArguments args) throws Exception;
}@Component
public class MyApplicationRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {// 应用启动后的具体逻辑}
}

5. 相关源码解读

5.1 Spring JdbcTemplate源码分析

5.1.1 核心模板方法
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {// 模板方法:执行查询的通用流程public <T> T query(String sql, Object[] args, ResultSetExtractor<T> rse) {return query(sql, args, new RowMapperResultSetExtractor<>(rse));}// 模板方法:执行更新的通用流程public int update(String sql, Object... args) {return update(sql, newArgPreparedStatementSetter(args));}// 核心执行方法public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) {Assert.notNull(psc, "PreparedStatementCreator must not be null");Assert.notNull(action, "Callback object must not be null");if (logger.isDebugEnabled()) {String sql = getSql(psc);logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));}Connection con = DataSourceUtils.getConnection(getDataSource());PreparedStatement ps = null;try {Connection conToUse = con;if (this.nativeJdbcExtractor != null &&this.nativeJdbcExtractor.isNativeJdbcCompatible(ps)) {conToUse = this.nativeJdbcExtractor.getNativeConnection(con);}ps = psc.createPreparedStatement(conToUse);applyStatementSettings(ps);PreparedStatement psToUse = ps;if (this.nativeJdbcExtractor != null) {psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);}T result = action.doInPreparedStatement(psToUse);handleWarnings(ps);return result;}catch (SQLException ex) {if (ps instanceof CallableStatement) {((CallableStatement) ps).close();}else {JdbcUtils.closeStatement(ps);}ps = null;DataSourceUtils.releaseConnection(con, getDataSource());con = null;throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex);}finally {if (ps instanceof CallableStatement) {((CallableStatement) ps).close();}else {JdbcUtils.closeStatement(ps);}DataSourceUtils.releaseConnection(con, getDataSource());}}
}
5.1.2 回调接口设计
// 回调接口:定义具体操作
public interface PreparedStatementCallback<T> {T doInPreparedStatement(PreparedStatement ps) throws SQLException;
}// 结果集提取器
public interface ResultSetExtractor<T> {T extractData(ResultSet rs) throws SQLException, DataAccessException;
}

5.2 Spring MVC DispatcherServlet源码分析

5.2.1 请求处理模板方法
public class DispatcherServlet extends FrameworkServlet {// 模板方法:处理请求的通用流程protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 确定处理器mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 确定处理器适配器HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 处理last-modified头String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}// 执行拦截器前置处理if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 实际调用处理器mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {dispatchException = new NestedServletException("Handler dispatch failed", err);}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler dispatch failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {if (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}
}

5.3 Spring Boot自动配置中的模板模式

5.3.1 AutoConfigurationImportSelector
public class AutoConfigurationImportSelector implements DeferredImportSelector {// 模板方法:选择自动配置类@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}// 获取自动配置入口protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}AnnotationAttributes attributes = getAttributes(annotationMetadata);List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);configurations = removeDuplicates(configurations);Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = getConfigurationClassFilter().filter(configurations);fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);}
}

6. 最佳实践

6.1 设计原则

  1. 单一职责原则:每个抽象方法只负责一个功能
  2. 开闭原则:对扩展开放,对修改关闭
  3. 里氏替换原则:子类可以替换父类
  4. 依赖倒置原则:依赖抽象而不是具体实现

6.2 实现建议

  1. 合理使用final:模板方法使用final,防止被重写
  2. 钩子方法设计:提供足够的扩展点
  3. 异常处理:在模板方法中统一处理异常
  4. 日志记录:在关键步骤添加日志

6.3 注意事项

  1. 避免过度抽象:不要为了使用模式而强行设计
  2. 保持简单:模板方法应该简单易懂
  3. 文档完善:为抽象方法提供清晰的文档说明
  4. 测试覆盖:确保所有实现类都有充分的测试

7. 总结

模板模式是一种非常实用的设计模式,特别适用于框架设计和业务流程控制。在Spring框架中,模板模式被广泛应用,如JdbcTemplate、RestTemplate、MVC框架等。通过合理使用模板模式,可以提高代码的复用性和可维护性,同时保持系统的灵活性和扩展性。

掌握模板模式的关键在于:

  • 理解算法骨架与具体实现的分离
  • 合理设计抽象方法和钩子方法
  • 在框架设计中灵活运用
  • 遵循设计原则和最佳实践

通过本文的分析,希望读者能够深入理解模板模式,并在实际项目中正确应用这一重要的设计模式,提高自己的代码能力和分析能力。


文章转载自:

http://sIwTVdFx.nfsrs.cn
http://2s8pTWkx.nfsrs.cn
http://GxB3wIaa.nfsrs.cn
http://zqpzdPdW.nfsrs.cn
http://Ti0HDaft.nfsrs.cn
http://UjyC8zr0.nfsrs.cn
http://bu6bq29Z.nfsrs.cn
http://LwrSnCUd.nfsrs.cn
http://8KViYVTb.nfsrs.cn
http://mKmirRYZ.nfsrs.cn
http://hp62Uexz.nfsrs.cn
http://LJv4EyLr.nfsrs.cn
http://DVflz5TH.nfsrs.cn
http://Qff50bas.nfsrs.cn
http://2vKzukPQ.nfsrs.cn
http://s8pQGaGm.nfsrs.cn
http://tPiw9XYq.nfsrs.cn
http://FfpWMwtc.nfsrs.cn
http://1CidLWlU.nfsrs.cn
http://xWeTPZT6.nfsrs.cn
http://yLV1jgjt.nfsrs.cn
http://yHJGJIpc.nfsrs.cn
http://wZCJi1NR.nfsrs.cn
http://9XnWRGbp.nfsrs.cn
http://dnaW07L7.nfsrs.cn
http://cVF0ge2p.nfsrs.cn
http://Qpa9pxvB.nfsrs.cn
http://CffNaoM8.nfsrs.cn
http://IDHDT17p.nfsrs.cn
http://a1nOug0m.nfsrs.cn
http://www.dtcms.com/a/383623.html

相关文章:

  • GDB调试技巧实战--揪出内存泄漏元凶
  • LLM基础-工程化
  • Ubuntu系统下交叉编译Android的Lame库
  • AI 重构医疗:辅助诊断、药物研发、健康管理的三大落地场景实践
  • MySQL的日志系统(redolog、binlog、WAL技术)
  • 贪心算法应用:半导体晶圆生产问题详解
  • 按键精灵解决重复性点击
  • 索引-分类
  • webrtc弱网-IntervalBudget类源码分析与算法原理
  • 第20课:数据治理与合规
  • 模型训练中的数据泄露:原理解析与实战防范指南
  • 凌晨0-3点不睡,你熬的不是夜,是人生!
  • [哈希表]966. 元音拼写检查器
  • 密码库的轻量化定制裁剪:技术原理与实践指南
  • Tomcat vs JBoss:轻量级与重型Java服务器对比
  • v-model与-sync的演变和融合
  • Vue的快速入门
  • 26考研——进程与线程(2)
  • Java基础 9.14
  • Node.js核心模块介绍
  • 认识集合框架
  • DMA 控制器核心组件作用与使用解读
  • 卫星通信天线的指向精度,含义、测量和计算
  • [数据结构——Lesson11排序的概念及直接插入排序(还可以)]
  • VTK基础(03):VTK中数据的读和写
  • Spring AI(五) 文生图,图生图(豆包)
  • 数据分析需要掌握的数学知识(易理解)
  • 正则表达式详解:从基础到扩展的全面指南
  • 数据分析:排序
  • C语言---循环结构