模板模式,全称是模板方法设计模式,是11种行为型模式之一。在 GoF 的《设计模式》一书中是这么定义的:Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.翻译成中文就是:在父类方法中定义算法的结构,将某些步骤推迟到子类。模板方法允许子类在不改变算法结构的情况下重新定义算法的某些步骤。这里的算法结构也可以是业务逻辑。
模板模式结构比较简单,不多说看代码
//模板类
public abstract calss Template {
//模板方法
public void templateMethod() {
//...省略步骤1代码
//步骤2
step2();
//...省略步骤3代码
}
protected abstract void step2(); //步骤2 等到子类去实现
}
模板模式遵循的设计原则和思想:
- DRY原则:模板方法提取了子类方法相同且稳定的结构
- 开闭原则:把会变化的步骤与稳定的结构解耦,符合开闭原则的保护稳定开放扩展的目标
模块方法在java可以说是到处可见,下面举几个例子
1.java.io.InputStream
public abstract class InputStream implements Closeable {
//...省略其他代码...
public abstract int read() throws IOException;
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
}
2.java.util.AbstractMap
public abstract class AbstractMap<K,V> implements Map<K,V> {
//...省略其他代码...
public abstract Set<Entry<K,V>> entrySet();
public V get(Object key) {
Iterator<Entry<K,V>> i = entrySet().iterator();
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return e.getValue();
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return e.getValue();
}
}
return null;
}
}
模板模式 VS 回调模式
首先用回调模式改造模板模式
//回调方法类
interface CallBack {
public void step2();
}
//模板类
public calss Template {
//模板方法
public void templateMethod(CallBack callback) {
//...省略步骤1代码
//步骤2
callback.step2();
//...省略步骤3代码
}
}
可以看到回调模式与模板模式通过继续的方式实现步骤2的扩展不同,回调模式是通过依赖的方式。因为是通过依赖的方式,所以任何类都可以成为模板类,而不只是抽象类。还有回调模式的模板方法与模板模式的模板方法一样都是定义算法的结构。因此可以认为回调模式是模板模式的一个变种。
回调模式遵循的设计原则和思想:
- DRY原则:模板方法提取了稳定的算法结构,把变化的步骤推迟到调用者去实现
- 开闭原则:把会变化的步骤与稳定的结构解耦,符合开闭原则的保护稳定开放扩展的目标
- 面向接口编程的思想:变化的步骤抽象成接口
- 多用组合少用继承的思想:依赖的方式是对象组合的其中一种方式
回调模式在java中运用举例:
-
org.springframework.jdbc.core.JdbcTemplate
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
//省略其他代码
public <T> T execute(ConnectionCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(this.obtainDataSource());
Object var10;
try {
Connection conToUse = this.createConnectionProxy(con);
var10 = action.doInConnection(conToUse);
} catch (SQLException var8) {
String sql = getSql(action);
DataSourceUtils.releaseConnection(con, this.getDataSource());
con = null;
throw this.translateException("ConnectionCallback", sql, var8);
} finally {
DataSourceUtils.releaseConnection(con, this.getDataSource());
}
return var10;
}
public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
Assert.notNull(sql, "SQL must not be null");
Assert.notNull(rse, "ResultSetExtractor must not be null");
if (this.logger.isDebugEnabled()) {
this.logger.debug("Executing SQL query [" + sql + "]");
}
class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
QueryStatementCallback() {
}
@Nullable
public T doInStatement(Statement stmt) throws SQLException {
ResultSet rs = null;
Object var3;
try {
rs = stmt.executeQuery(sql);
var3 = rse.extractData(rs);
} finally {
JdbcUtils.closeResultSet(rs);
}
return var3;
}
public String getSql() {
return sql;
}
}
return this.execute((StatementCallback)(new QueryStatementCallback()));
}
}
两种模式的比较
- 回调模式更加灵活,实现由调用者把控,变化更多,稳定些不如模板模式。
- 模板模式子类需要先写好子类,相对变化更少,更加稳定。