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
- An increase in the number of classes, each abstract class requires a subclass to implement, resulting in an increase in the number of classes.
- The increase in the number of classes indirectly increases the complexity of the system implementation.
- 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