1、模板設計模式
趙本山問宋丹丹: “如何把大象放進冰箱?”宋丹丹回答:“第一步:打開冰箱門,第二步:把大象塞進 冰箱,第三步:關閉冰箱門”。趙本山再問:“怎麼把長勁鹿放進冰箱?”宋丹丹答: “第一步:打開冰箱門,第二步:把大象拿出來,第三步:把長勁鹿塞進去,第四步: 關閉冰箱門”(如下圖所示),這些都是模板模式的體現。
模板設計模式通常又叫做模板方法設計模式,是指定義個算法骨架,允許子類爲一個或者多個步驟提供實現。模板方法使得子類在不改變算法結構的情況下,重新定義算法的某些步驟,屬於行爲設計模式。
適用場景:
(1)一次性實現算法的不變部分,將可變的部分留給子類實現;
(2)各個子類中提取出公共行爲,向上提取到公共的父類中,從而實現 代碼複用。
模板模式在經典框架mybaties中的的應用:
BaseExecutor是一個基礎類,實現了大部分的SQL實行流程和邏輯,如獲取事物、關閉連接等,將處理不同的細節抽象,交給子類實現,下面是子類需要試下的抽象方法。
public abstract class BaseExecutor implements Executor {
protected abstract int doUpdate(MappedStatement var1, Object var2) throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean var1) throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, BoundSql var5) throws SQLException;
}
下面看看BaseExecutor的子類有那些?
下面看看doUpdate方法的實現
public class SimpleExecutor extends BaseExecutor {
public SimpleExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
int var6;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var6 = handler.update(stmt);
} finally {
this.closeStatement(stmt);
}
return var6;
}
.................
}
public class BatchExecutor extends BaseExecutor {
public static final int BATCH_UPDATE_RETURN_VALUE = -2147482646;
private final List<Statement> statementList = new ArrayList();
private final List<BatchResult> batchResultList = new ArrayList();
private String currentSql;
private MappedStatement currentStatement;
public BatchExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
Statement stmt;
if (sql.equals(this.currentSql) && ms.equals(this.currentStatement)) {
int last = this.statementList.size() - 1;
stmt = (Statement)this.statementList.get(last);
BatchResult batchResult = (BatchResult)this.batchResultList.get(last);
batchResult.addParameterObject(parameterObject);
} else {
Connection connection = this.getConnection(ms.getStatementLog());
stmt = handler.prepare(connection);
this.currentSql = sql;
this.currentStatement = ms;
this.statementList.add(stmt);
this.batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
handler.parameterize(stmt);
handler.batch(stmt);
return -2147482646;
}
...............
}
public class ReuseExecutor extends BaseExecutor {
private final Map<String, Statement> statementMap = new HashMap();
public ReuseExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, (ResultHandler)null, (BoundSql)null);
Statement stmt = this.prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
}
................
}
JDK AbstractList 模板模式的應用:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
abstract public E get(int index);
}
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
transient Object[] elementData;
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];
}
}
public class ArrayQueue<T> extends AbstractList<T> {
public T get(int i) {
int size = size();
if (i < 0 || i >= size) {
final String msg = "Index " + i + ", queue size " + size;
throw new IndexOutOfBoundsException(msg);
}
int index = (head + i) % capacity;
return queue[index];
}
}
優點:
(1)將相同處理邏輯的代碼放入到父類中,提高代碼的複用性;
(2)處理不同細節的實現放在子類中,通過對子類擴展功能,提高代碼的擴展性;
(3)複合開閉原則
缺點:
(1)每個抽象類都需要子類實現,增加了類的個數;
(2)類個數的增加,導致系統複雜度提升;
(3)繼承自身的缺點,父類增加抽象方法,所有子類都要實現
思考:
模板模式除了繼承方式以外,還有那些實現方式?
模板是的特點,流程標準化,處理細節不同,通過子類來擴展功能, 可以通過動態代理設計模式,
實現子類功能擴展,在子類方法前,調用固定流程方法實現,增強子類方法,從而實現模板設計模式的效果。
2、適配器設計模式
適配器模式是將一個接口轉換成另外一個接口,使原本不兼容的接口和類,可以工作,屬於結構性設計模式。
使用的業務場景:
(1)已經存在的類,方法和需求不匹配的情況‘
(1)一般情況下:適配器模式不是設計階段考慮的,而是系統更新升級,新的標準兼顧老版本使用。
如生活中的兩孔插頭轉三孔插頭; IPhone7的耳機孔標準採用了充電接口, 爲了讓Iphone6的圓孔耳機能夠在Iphone7上使用;居民用電220V,適配手機充電5V等。
適配器模式在軟件系統中的使用場景,如開始只有賬戶密碼登錄,現在升級支持三方登錄,兼容賬戶密碼登錄,如微信、QQ、支付寶、新浪等;
適配器在Spring中的應用:
(1)SpringAOP中AdvisorAdaptor與它的三個子類,MethodBeforeAdvisorAdaptor、AfterReturningAdviceAdapter、ThrowsAdviceAdapter
public interface AdvisorAdapter {
boolean supportsAdvice(Advice var1);
MethodInterceptor getInterceptor(Advisor var1);
}
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
MethodBeforeAdviceAdapter() {
}
public boolean supportsAdvice(Advice advice) {
return advice instanceof MethodBeforeAdvice;
}
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
AfterReturningAdviceAdapter() {
}
public boolean supportsAdvice(Advice advice) {
return advice instanceof AfterReturningAdvice;
}
public MethodInterceptor getInterceptor(Advisor advisor) {
AfterReturningAdvice advice = (AfterReturningAdvice)advisor.getAdvice();
return new AfterReturningAdviceInterceptor(advice);
}
}
class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {
ThrowsAdviceAdapter() {
}
public boolean supportsAdvice(Advice advice) {
return advice instanceof ThrowsAdvice;
}
public MethodInterceptor getInterceptor(Advisor advisor) {
return new ThrowsAdviceInterceptor(advisor.getAdvice());
}
}
(2)SpringMVC中的HandlerAdaptor類,與它的子類。
適配器調用關鍵代碼在DispatchServlet的doDospath()方法中,如以下源碼:
public class DispatcherServlet extends FrameworkServlet {
.............
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
this.noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (this.logger.isDebugEnabled()) {
this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
.............
}
getHandlerAdaptor()方法中循環調用了supports()方法判斷兼容性,迭代集合中Adaptor初始化是早已經複製。
優點:
(1)能提高類的透明性和複用
(2)目標類和適配器解耦,提供程序擴展性;
(3)複合開閉原則
缺點:
(1)適配器編寫過程中需要全面考慮,增加系統複雜性;
(2)增加代碼理解難度,降低代碼可讀性,過多使用適配器會使代碼混亂