大B:“剛纔說模板方法模式運用於一個業務對象。事實上,框架頻繁使用模板方法模式,使得框架實現對關鍵邏輯的集中控制。”
大B:“我們需要爲基本Spring的應用做一個測試用例的基類。用於對類的方法進行單元測試。我們知道Spring應用把需要用到的對象都定義在外部的xml文件中,也稱爲context。”
大B:“通常我們會把context分割成多個小的文件,以便於管理。在測試時我們需要讀取context文件,但是並不是每次都讀取所有的文件。讀取這些文件是很費時間的。所以我們想把它緩存起來,只要這個文件被讀取過一次,我們就把它們緩存起來。所以我們通過擴展Junit的TestCase類來完成一個測試基類。我們需要實現緩存的邏輯,其它開發人員只需要實現讀取配置文件的方法即可。它不用管是否具有緩存。”
代碼:
publicAbstractCacheContextTestsextendsTestCase{
privatestaticMapcontextMap=newHashMap();
protectedConfigurableApplicationContextapplicationContext;
protectedbooleanhasCachedContext(ObjectcontextKey){
returncontextKeyToContextMap.containsKey(contextKey);
}
protectedConfigurableApplicationContextgetContext(Objectkey){
StringkeyString=contextKeyString(key);
ConfigurableApplicationContextctx=(ConfigurableApplicationContext)contextKeyToContextMap.get(keyString);
if(ctx……null){
if(keyinstanceofString[]){
ctx=loadContextLocations((String[])key);
}
contextKeyToContextMap.put(keyString,ctx);
}
returnctx;
}
protectedStringcontextKeyString(ObjectcontextKey){
if(contextKeyinstanceofString[]){
returnStringUtils.arrayToCommaDelimitedString((String[])contextKey);
}
else{
returncontextKey.toString();
}
}
protectedConfigurableApplicationContextloadContextLocations(String[]locations){
returnnewClassPathXmlApplicationContext(locations);
}
//覆寫TestCase的setUp方法,在運行測試方法之前從緩存中讀取context文件,如果緩存中不存在則初始化applicationContext,並放入緩存。
protectedfinalvoidsetUp()throwsException{
String[]contextFiles=getConfigLocations();
applicationContext=getContext(contextFiles);
}
//讀取context文件,由子類實現
protectedabstractString[]getConfigLocations();
}
大B:“rendercode();這樣子類只需要去實現getConfigLocations方法,提供需要讀取的配置文件字符數組就可以了。至於怎麼去讀取context文件內容,怎麼實現緩存,則無需關心。AbstractCacheContextTests保證在運行所有測試之前去執行讀取context文件的動作。注意這裡setUp方法被聲明爲protected,是因爲setUp方法是TestCase類的方法。在這裡setUp方法被定義爲final了,是確保子類不能去覆寫這個方法,從而保證了父類控制的邏輯。”
小A:“如果使用過Junit會發生什麼問題?”
大B:“TestCase的setUp方法,就是在這個測試類的測試方法運行之前作一些初始化動作。如創建一些所有測試方法都要用到的公共對象等。在這裡把setUp方法聲明爲final之後,子類再也無法去擴展它了,子類同時還需要一些額外的初始化動作就無法實現了。可能你會說:‘把setUp方法的final修飾符去掉就可以了隘。這樣是可以的,但是去掉final修飾符後,子類是可以覆寫setUp方法,而去執行一些額外的初始化。而可怕的是,父類從此失去了必須讀取context文件及緩存context內容的邏輯。爲了解決這個問題,可以實現一個空方法onSetUp。在setUp方法中調用onSetUp方法。這樣子類就可以通過覆寫onSetUp方法來進行額外的初始化。”
//覆寫TestCase的setUp方法,在運行測試方法之前從緩存中讀取context文件,如果緩存中不存在則初始化applicationContext,並放入緩存。
代碼:
protected
nclass=“keyword”>finalvoidsetUp()throwsException{
String[]contextFiles=getConfigLocations();
applicationContext=getContext(contextFiles);
onSetUp();
}
protectedvoidonSetUp(){
}
//讀取context文件,由子類實現
protectedabstractString[]getConfigLocations();
}
rendercode();
小A:“爲什麼不把onSetUp聲明爲abstract呢?”
大B:“這是因爲子類不一定總是需要覆寫onSetUp方法。可以說onSetUp方法是爲了對setUp方法的擴展。像onSetUp這樣的空方法就稱之爲勾子方法(HookMethod)。”