Template pattern

Introduction to the

Reference: en.wikipedia.org/wiki/Templa…

The template method pattern, often called the template method pattern, defines the skeleton of an algorithm and allows subclasses to provide implementations for one or more steps.

Enables subclasses to redefine certain steps of an algorithm without changing the structure of the algorithm.

Behavioral design pattern

Generic class diagrams

public abstract class AbstractClass {
    protected void step1(a) {
        System.out.println("AbstractClass:step1");
    }

    protected void step2(a) {
        System.out.println("AbstractClass:step2");
    }

    protected void step3(a) {
        System.out.println("AbstractClass:step3");
    }

    // Declare it final to avoid subclass overwriting
    public final void templateMehthod(a) {
        this.step1();
        this.step2();
        this.step3(); }}Copy the code

Be aware of the public methods. Process methods are generally final to avoid being overridden by subclasses

Scenes from life

This fixed procedure

  • Put the elephant in the refrigerator

    • Open the door
    • The elephant goes into the fridge
    • Shut down the
  • stir-fry

The fixed process is placed at the top, and the concrete implementation class implements the details

case

Case code from github.com/iluwatar/ja…

A thief steals things in several steps:

  • Looking for targets
  • Confuse the target
  • Lay hands on

So let’s define a thief

public class HalflingThief {

  private StealingMethod method;

  public HalflingThief(StealingMethod method) {
    this.method = method;
  }

  public void steal(a) {
    method.steal();
  }

  public void changeMethod(StealingMethod method) {
    this.method = method; }}Copy the code

Because stealing is a fixed process, we use the template method, but there are many ways to steal, so we open the hook Steal method

public abstract class StealingMethod {

  private static final Logger LOGGER = LoggerFactory.getLogger(StealingMethod.class);

  protected abstract String pickTarget(a);

  protected abstract void confuseTarget(String target);

  protected abstract void stealTheItem(String target);


  public void steal(a) {
    String target = pickTarget();
    LOGGER.info("The target has been chosen as {}.", target); confuseTarget(target); stealTheItem(target); }}Copy the code

Hit and run

public class HitAndRunMethod extends StealingMethod {
  
    private static final Logger LOGGER = LoggerFactory.getLogger(HitAndRunMethod.class);
  
    @Override
    protected String pickTarget(a) {
      return "old goblin woman";
    }
  
    @Override
    protected void confuseTarget(String target) {
      LOGGER.info("Approach the {} from behind.", target);
    }
  
    @Override
    protected void stealTheItem(String target) {
      LOGGER.info("Grab the handbag and run away fast!"); }}Copy the code

Work under the table

  public class SubtleMethod extends StealingMethod {
  
    private static final Logger LOGGER = LoggerFactory.getLogger(SubtleMethod.class);
  
    @Override
    protected String pickTarget(a) {
      return "shop keeper";
    }
  
    @Override
    protected void confuseTarget(String target) {
      LOGGER.info("Approach the {} with tears running and hug him!", target);
    }
  
    @Override
    protected void stealTheItem(String target) {
      LOGGER.info("While in close contact grab the {}'s wallet.", target); }}Copy the code

test



public class App {

  public static void main(String[] args) {
    HalflingThief thief = new HalflingThief(new HitAndRunMethod());
    thief.steal();
    thief.changeMethod(newSubtleMethod()); thief.steal(); }}Copy the code

Manifest in source code

JDBCTemplate

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
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.

AbstractList

AbstractList defines a bunch of abstract methods to keep subclasses from having to write again

For example the Add method

public boolean add(E e) {
  add(size(), e);
  return true;
}
public void add(int index, E element) {
  throw new UnsupportedOperationException();
}
Copy the code

Subclasses can be overridden to implement their own logic

HttpServlet

protected void service(HttpServletRequest req, HttpServletResponse resp)
  throws ServletException, IOException {

  String method = req.getMethod();

  if (method.equals(METHOD_GET)) {
    long lastModified = getLastModified(req);
    if (lastModified == -1) {
      // servlet doesn't support if-modified-since, no reason
      // to go through further expensive logic
      doGet(req, resp);
    } else {
      long ifModifiedSince;
      try {
        ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
      } catch (IllegalArgumentException iae) {
        // Invalid date header - proceed as if none was set
        ifModifiedSince = -1;
      }
      if (ifModifiedSince < (lastModified / 1000 * 1000)) {
        // If the servlet mod time is later, call doGet()
        // Round down to the nearest second for a proper compare
        // A ifModifiedSince of -1 will always be less
        maybeSetLastModified(resp, lastModified);
        doGet(req, resp);
      } else{ resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); }}}else if (method.equals(METHOD_HEAD)) {
    long lastModified = getLastModified(req);
    maybeSetLastModified(resp, lastModified);
    doHead(req, resp);

  } else if (method.equals(METHOD_POST)) {
    doPost(req, resp);

  } else if (method.equals(METHOD_PUT)) {
    doPut(req, resp);

  } else if (method.equals(METHOD_DELETE)) {
    doDelete(req, resp);

  } else if (method.equals(METHOD_OPTIONS)) {
    doOptions(req,resp);

  } else if (method.equals(METHOD_TRACE)) {
    doTrace(req,resp);

  } else {

    String errMsg = lStrings.getString("http.method_not_implemented");
    Object[] errArgs = new Object[1];
    errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); }}Copy the code

As you can see, the service method is the template method, and the specific DoGet and DoPost methods are left to subclasses to implement. Subclasses do not need to rewrite this lengthy code.

Mybatis BaseExecute

Executor is a basic SQL execution class that implements most of the SQL execution logic and then leaves several methods to subclass customization.

For example,

  • doUpdate
  • doFlushStatement
  • doQuery
  • doQueryCursor

Query methods

See org. Apache. Ibatis. Executor. BaseExecutor# commit his submission methods will call refresh is committed to the database, but the timing of the concrete implementation may submit is different, for example SimpleExecutor is immediately submitted, But the BatchExecutor is a bunch of stores and then commits all together.

public void commit(boolean required) throws SQLException {
  if (closed) {
    throw new ExecutorException("Cannot commit, transaction is already closed");
  }
  clearLocalCache();
  flushStatements();
  if(required) { transaction.commit(); }}public List<BatchResult> flushStatements(boolean isRollBack) throws SQLException {
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  return doFlushStatements(isRollBack);
}

protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
  throws SQLException;
Copy the code

Look at doFlushStatements for the specific class at this point

ReuseExecutor

org.apache.ibatis.executor.ReuseExecutor#doFlushStatements

  private final Map<String, Statement> statementMap = new HashMap<>();

public List<BatchResult> doFlushStatements(boolean isRollback) {
  for (Statement stmt : statementMap.values()) {
    closeStatement(stmt);
  }
  statementMap.clear();
  return Collections.emptyList();
}
Copy the code

ReuseExecutor needs to reuse statements with the same Statement, so it needs to clear them out when submitting

SimpleExecutor

org.apache.ibatis.executor.SimpleExecutor#doQuery

  @Override
  public List<BatchResult> doFlushStatements(boolean isRollback) {
    return Collections.emptyList();
  }
Copy the code

SimpleExecutor does nothing

BatchExecutor

org.apache.ibatis.executor.BatchExecutor#doQuery

public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    try {
      List<BatchResult> results = new ArrayList<>();
      if (isRollback) {
        return Collections.emptyList();
      }
      for (int i = 0, n = statementList.size(); i < n; i++) {
        Statement stmt = statementList.get(i);
        applyTransactionTimeout(stmt);
        BatchResult batchResult = batchResultList.get(i);
        try {
          batchResult.setUpdateCounts(stmt.executeBatch());
          MappedStatement ms = batchResult.getMappedStatement();
          List<Object> parameterObjects = batchResult.getParameterObjects();
          KeyGenerator keyGenerator = ms.getKeyGenerator();
          if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {
            Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
            jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);
          } else if(! NoKeyGenerator.class.equals(keyGenerator.getClass())) {//issue #141
            for (Object parameter : parameterObjects) {
              keyGenerator.processAfter(this, ms, stmt, parameter); }}// Close statement to close cursor #1109
          closeStatement(stmt);
        } catch (BatchUpdateException e) {
          StringBuilder message = new StringBuilder();
          message.append(batchResult.getMappedStatement().getId())
              .append(" (batch index #")
              .append(i + 1)
              .append(")")
              .append(" failed.");
          if (i > 0) {
            message.append("")
                .append(i)
                .append(" prior sub executor(s) completed successfully, but will be rolled back.");
          }
          throw new BatchExecutorException(message.toString(), e, results, batchResult);
        }
        results.add(batchResult);
      }
      return results;
    } finally {
      for (Statement stmt : statementList) {
        closeStatement(stmt);
      }
      currentSql = null; statementList.clear(); batchResultList.clear(); }}Copy the code

BatchExecutor needs to commit in batches

So you can see that the common methods are implemented in the parent class, and the specific differences are left up to the subclasses to implement the hook methods

conclusion

The difference between template and policy patterns

Template subclasses can interfere with implementation, but the underlying flow is already defined

Policies do not interfere with the process; the implementation has to be chosen

The advantages and disadvantages

advantages

  • Encapsulate invariant parts and extend variable parts
    • New function, just need a new subclass to implement the corresponding function method on the line
  • Extract common parts of code for easy maintenance
    • Otherwise the maintenance staff would have to look around for similar code to fix a bug!
  • The behavior is controlled by the parent class and implemented by the child class
    • The base method is implemented by subclasses, so subclasses can be extended to add functionality in accordance with the open closed principle.

disadvantages

  1. An increase in the number of classes, each abstract class requires a subclass to implement, resulting in an increase in the number of classes.
  2. The increase in the number of classes indirectly increases the complexity of the system implementation.
  3. Inheritance has its own disadvantages. If the parent class adds a new abstract method, all subclasses have to change it

Applicable scenario

  • Multiple subclasses have common methods and basically the same logic.
  • For important and complex algorithms, the core algorithm can be designed as a template method, and the surrounding details are implemented by each subclass.
  • In refactoring, the template method pattern is a frequently used pattern that pulls the same code into a parent class and then constrains the behavior with hook functions

Other questions

How does a parent class call a child class’s method

  • Pass the subclass to the parent class’s parameter construct and call.
  • Use reflection method call, you use reflection and who can not call
  • A parent class calls a static method of a child class.

Gitee, please give me a Star