设计模式-模板模式详解

设计模式-模板模式详解

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 {
    @Override
    public 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 {
  
    // 模板方法:选择自动配置类
    @Override
    public 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框架等。通过合理使用模板模式,可以提高代码的复用性和可维护性,同时保持系统的灵活性和扩展性。

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值