define

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses define certain steps of an algorithm without changing the algorithm’s structure.

This means defining the framework of an algorithm in an operation and deferring some steps to a subclass so that the subclass can define specific steps of an algorithm without changing its structure.

purpose

  1. Using the template method pattern avoids writing repetitive code so that developers can focus on implementing the core business logic
  2. Resolve the problem of inheritance contradiction between interface and interface implementation class

The code for

For example, if there is a query in the business logic, it is usually better to define a CommonQueryService interface and implement it directly, such as:

public class CommonQueryServiceSimpleImpl implements CommonQueryService {
    @Override
    public CommonResult<CommonInfo> queryByUser(CommonRequest request) {

        /** * Business logic */
        CommonResult<CommonInfo> result = new CommonResult<>();

        returnresult; }}Copy the code

If there is only one query scenario it is fine, but if there are multiple query scenarios with different dimensions each time, how do you avoid writing repetitive code? Here can use the template method pattern, put the query scenario abstracts a template, divided into parameter calibration – > query preprocessing rear – > execute business logic – > query processing, the advantage is that the only need to rewrite the query scene after several logical line, about the abnormal log capture, failure to print all the general logic in the template class.

Template class

public class CommonQueryTemplate {

    private final static String BIZ_DESC = "Enquiry Business";

    public static <R, M> CommonResult<M> query(R request, AbstractInvokeExecutor
       
         invoke)
       ,> {

        CommonResult<M> result = new CommonResult<M>();

        /** * type some log */
        
        try {
            
            // Service verification method
            invoke.checkParam();

            CommonQueryContext context = new CommonQueryContext();

            / / pretreatment
            invoke.preProcess(request, context);

            // Execute business logic
            M model = invoke.query(context);

            // Encapsulate the result of business processing
            result.setResultObject(model);

            // post-processing
            invoke.postProcess(result, context);
        } catch (Exception e){
            
        } catch (Throwable t){
            
        } finally {
            // You can type some log, such as the length of the run time
        }
        returnresult; }}Copy the code

The implementation is a little different here; the template class is not an abstract class. The idea here is to define an additional AbstractInvokeExecutor class that wraps the methods to be implemented in InvokeExecutor.

AbstractInvokeExecutor

The generic AbstractInvokeExecutor abstract class

public abstract class AbstractInvokeExecutor<R.M> {
    /** ** Check method */
    protected abstract void checkParam(a);

    /** * Request preprocessing */
    protected abstract void preProcess(R request, CommonQueryContext context);

    /** * Business logic execution method */
    protected abstract M query(CommonQueryContext context);

    /** ** post-processing */
    protected abstract void postProcess(CommonResult<M> result, CommonQueryContext context);
}
Copy the code

CommonQueryServiceImpl

The real implementation of the interface is simply instantiating an AbstractInvokeExecutor to implement the abstract method

public class CommonQueryServiceImpl implements CommonQueryService {
    @Override
    public CommonResult<CommonInfo> queryByUser(CommonRequest request) {

        CommonResult<CommonInfo> result = CommonQueryTemplate.query(request,
                new AbstractInvokeExecutor<CommonRequest, CommonInfo>() {
                    @Override
                    protected void checkParam(a) {}@Override
                    protected void preProcess(CommonRequest request, CommonQueryContext context) {}@Override
                    protected CommonInfo query(CommonQueryContext context) {
                        return null;
                    }

                    @Override
                    protected void postProcess(CommonResult<CommonInfo> result, CommonQueryContext context) {}});return null; }}Copy the code

The source code, for example,

JDBCTemplate

query

    public <T> T query(
            PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
            throws DataAccessException {

        Assert.notNull(rse, "ResultSetExtractor must not be null");
        logger.debug("Executing prepared SQL query");

        return execute(psc, new PreparedStatementCallback<T>() {
            @Override
            public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
                ResultSet rs = null;
                try {
                    if(pss ! =null) {
                        pss.setValues(ps);
                    }
                    rs = ps.executeQuery();
                    ResultSet rsToUse = rs;
                    if(nativeJdbcExtractor ! =null) {
                        rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
                    }
                    return rse.extractData(rsToUse);
                }
                finally {
                    JdbcUtils.closeResultSet(rs);
                    if (pss instanceofParameterDisposer) { ((ParameterDisposer) pss).cleanupParameters(); }}}}); }Copy the code

extractData

public List<T> extractData(ResultSet rs) throws SQLException {
  List<T> results = (this.rowsExpected > 0 ? new ArrayList<T>(this.rowsExpected) : new ArrayList<T>());
  int rowNum = 0;
  while (rs.next()) {
    results.add(this.rowMapper.mapRow(rs, rowNum++));
  }
  return results;
}
Copy the code

As you can see, the fixed code has been written for us to do things like get connection, get driver, get statement, close connection, etc.

We just need to implement the corresponding RowMapper ourselves to do the result set mapping.

conclusion

advantages

  • The new functionality does not require rewriting all the logical code, but simply implementing the methods of the abstract class
  • The behavior is controlled by the parent class, and the subclass increases the corresponding function by extension, in accordance with the open closed principle

disadvantages

  • The disadvantage of inheritance is that if the parent class adds a new abstract method, all subclasses have to change it