一 模板方法模式
模板方法模式(Template Method Pattern)是設計模式中比較簡單的一種,但在工作中使用的場景卻比較多,面試時也會被經常被問到,其基本定義如下:
Define the skeleton of an algorithm in an operation,deferring some steps tosubclasses.Template Method lets subclasses redefine certain steps of analgorithm without changing the algorithm’s structure.(定義一個操作中的算法的框架,而將一些步驟延遲到子類中。使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。)
其核心要義在於,搭好一個框架,但部分操作由子類實現,類圖如下:
所謂的框架就是這個抽象類:AbstractClass,這個類的作用只是一個框架,所以不能被單獨使用,其基本結構如下面代碼所示:
public abstract class AbstractClass {
// 共同的且繁瑣的操作
private void baseOperation() {
// do something
}
// 由子類定製的操作
protected abstract void customOperation();
// 模板方法定義的框架
public final void templateMethod() {
baseOperation();
customOperation();
}
}
templateMethod
定義了一種執行序列,在baseOperation
中處理完成了一系列繁瑣但無需子類關心的操作,而customOperation
可以由子類去實現。
子類的代碼如下:
public class ConcreteClassOne extends AbstractClass {
@Override
protected void customOperation() {
// do custom things
}
}
這樣當有新的自定義行爲時,只需要增加新的實現類即可,對已有的客戶端類沒影響或者影響很小。
public class Client {
public static void main(String[] args) {
AbstractClass c1 = new ConcreteClassOne();
AbstractClass c2 = new ConcreteClassTwo();
applyTemplate(c1);
}
public static void applyTemplate(AbstractClass abstractClass) {
abstractClass.templateMethod();
}
}
二 模板方法模式的優缺點
2.1 優點
●封裝不變部分,擴展可變部分,把認爲是不變部分的算法封裝到父類實現,而可變部分的則可以通過繼承來繼續擴展。
●提取公共部分代碼,便於維護
●符合開閉原則,行爲由父類控制,而基本方法是由子類實現的,因此子類可以通過擴展的方式增加相應的功能。
2.2 缺點
- 由子類實現,子類執行的結果影響了父類的結果,也就是子類對父類產生了影響。
- 可能引起子類氾濫和爲了繼承而繼承的問題
三 模板方法結合回調函數
由於模板方法模式存在上述缺點,爲了解決這兩個問題,利用回調函數代替子類繼承是一個很好的解決方案。其類圖如下:
此時,Template
類仍然只是提供了一個框架,其基本功能和AbstractClass
類似,不同之處在於,Template
不是抽象類,而是一個具體類(一般聲明爲final類),其代碼如下:
public final class Template {
private void baseOperation() {
// do something
}
public void templateMethod(Callback callback) {
baseOperation();
callback.customOperation();
}
}
Callback及其子類代碼如下:
public interface Callback {
void customOperation();
}
public class SubCallback implements Callback {
@Override
public void customOperation() {
// do custom things
}
}
客戶端類變化也很小,其代碼如下:
public class Client {
public static void main(String[] args) {
Template template = new Template();
applyTemplate(template);
}
public static void applyTemplate(Template template) {
Callback c1 = new SubCallback();
template.templateMethod(c1);
}
}
這裏我們可以看到,結合回調函數以後,Template
是一個final
類,無法被繼承,也就不存在子類行爲影響父類結果的問題,而Callback
是一個接口,爲了繼承而繼承的問題消失了。
四 應用
模板方法結合回調函數在Spring中十分常見,比如JdbcTemplate就是一種典型的應用,JdbcTemplate部分代碼如下:
public class JdbcTemplate {
public <T> T execute(StatementCallback<T> action) {
Connection con = getConnection();
Statement stmt = null;
try {
stmt = con.createStatement();
applyStatementSettings(stmt);
// 調用Callback的自定義行爲
T result = action.doInStatement(stmt);
handleWarnings(stmt);
return result;
}
catch (SQLException ex) {
// 異常處理
}
finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
}
// Callback接口定義
@FunctionalInterface
public interface StatementCallback<T> {
@Nullable
T doInStatement(Statement stmt) throws SQLException, DataAccessException;
}
// JdbcTemplate提供的一個默認callback實現
class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {
public Object doInStatement(Statement stmt) throws SQLException {
stmt.execute(sql);
return null;
}
// 以下省略。。。
}