標籤:Lucene style blog http java color
《西遊降魔》裡面的《兒歌三百首》裡面有首兒歌叫做《春天在哪裡》
歌詞是這樣的:
春天在哪裡
春天在哪裡
春天就在小朋友的眼睛裡
通過俺的渣英語翻譯之後是這樣的:
where spring is
where spring is
the fucking spring is
in javatar‘s eyes
yo
yo
check it out
我相信,java程式員已經意識到我說的春天是什麼了:)
不過spring跟我們現在說的東東有關係嗎?暫時還沒有-_-b
言歸正傳,開始我們上一章節所說的,把問題搞複雜點兒。
我們假設這個搜尋業務需要lucene和mysql的支援。
通過lucene的檢索獲得文檔ID,然後根據ID去mysql資料庫查詢,獲得文檔的標題和常值內容。
讓我們從需求開始,首先操作 SearchServiceInRealBiz 類:
package cn.com.sitefromscrath.service;import java.util.ArrayList;import java.util.List;import cn.com.sitefromscrath.entity.Result;public class SearchServiceInRealBiz implements SearchService {public List search(String keywords) {int[] idlist = findDocIDs(keywords);List<Result> results = getResultsByDocIDs(idlist);return results;}}
當然,你會看到你的eclipse會有錯誤提示資訊:
因為這兩個方法沒有定義。但是沒有關係,eclipse本身內建工具,
自動產生的程式碼是這樣子的:
實現他們就好啦:)
我們按照先前的開發流程,給出類比實現先:
package cn.com.sitefromscrath.service;import java.util.ArrayList;import java.util.List;import cn.com.sitefromscrath.entity.Result;public class SearchServiceInRealBiz implements SearchService {public List search(String keywords) {int[] idlist = findDocIDs(keywords);List<Result> results = getResultsByDocIDs(idlist);return results;}private List<Result> getResultsByDocIDs(int[] idlist) {List<Result> results = new ArrayList<Result>(idlist.length);for(int i = 0; i < idlist.length; i++) {int id = idlist[i];String title = "result " + id;String content = "something..................";results.add(new Result(title, content));}return results;}private int[] findDocIDs(String keywords) {return new int[]{1, 2, 3, 4};}}請記住,每一步我們都應該去做測試,這裡就不一一贅述。
比如,我們可以把BeanFactory run一次,看看main方法的輸出會不會發生非預期的變化。
現在,雖然我沒有運行tomcat查看網頁,但是我能肯定,網頁所展示的內容一定是正確的。
由於java程式員的本性發作,我覺得寫一個DAO層,然後。。。當然是介面和實現分離蝦米的。。。
lucene的實現(類比階段):
介面:
package cn.com.sitefromscrath.dao;public interface LuceneDAO {public int[] findDocIDs(String keywords);}
實現:
package cn.com.sitefromscrath.dao;public class LuceneDAOMock implements LuceneDAO {@Overridepublic int[] findDocIDs(String keywords) {return new int[]{1, 2, 3, 4};}}
Mysql的實現(類比階段):
介面:
package cn.com.sitefromscrath.dao;import java.util.List;import cn.com.sitefromscrath.entity.Result;public interface MysqlDAO {public List<Result> getResultsByDocIDs(int[] idlist);}
實現:
package cn.com.sitefromscrath.dao;import java.util.ArrayList;import java.util.List;import cn.com.sitefromscrath.entity.Result;public class MysqlDAOMock implements MysqlDAO {@Overridepublic List<Result> getResultsByDocIDs(int[] idlist) {List<Result> results = new ArrayList<Result>(idlist.length);for(int i = 0; i < idlist.length; i++) {int id = idlist[i];String title = "result " + id;String content = "something..................";results.add(new Result(title, content));}return results;}}
然後,我們把 SearchServiceInRealBiz 的代碼從新組織一次,將:
package cn.com.sitefromscrath.service;import java.util.ArrayList;import java.util.List;import cn.com.sitefromscrath.entity.Result;public class SearchServiceInRealBiz implements SearchService {public List search(String keywords) {int[] idlist = findDocIDs(keywords);List<Result> results = getResultsByDocIDs(idlist);return results;}private List<Result> getResultsByDocIDs(int[] idlist) {}private int[] findDocIDs(String keywords) {}}
替換為:
public class SearchServiceInRealBiz implements SearchService {public List search(String keywords) {//int[] idlist = findDocIDs(keywords);//List<Result> results = getResultsByDocIDs(idlist);LuceneDAO luceneDAO = new LuceneDAOMock();int[] idlist = luceneDAO.findDocIDs(keywords);MysqlDAO mysqlDAO = new MysqlDAOMock();List<Result> results = mysqlDAO.getResultsByDocIDs(idlist);return results;}}
測試,stdout / eclipse console 輸出無誤。
[result 1]something..................[result 2]something..................[result 3]something..................[result 4]something..................
當然,我們同樣會發現一個問題,我們現在的類是類比的資料啊,以後怎麼切換呢?
還好,有工廠。既然我們在工廠裡切換了
SearchService
那麼,
LuceneDAO MysqlDAO
同樣可以在那裡處理,於是,同樣執行我上面提到的流程,
從需求開始,我需要一個什麼樣的方法,那就先定義什麼方法,然後利用eclipse工具產生方法的骨架 method skeleton,然後實現它。
首先改寫 SearchServiceInRealBiz :
然後,實現方法骨架:
package cn.com.sitefromscrath;import java.util.List;import javax.xml.rpc.ServiceFactory;import cn.com.sitefromscrath.dao.LuceneDAO;import cn.com.sitefromscrath.dao.LuceneDAOMock;import cn.com.sitefromscrath.dao.MysqlDAO;import cn.com.sitefromscrath.dao.MysqlDAOMock;import cn.com.sitefromscrath.entity.Result;import cn.com.sitefromscrath.service.SearchService;import cn.com.sitefromscrath.service.SearchServiceMock;import cn.com.sitefromscrath.service.SearchServiceInRealBiz;public class BeanFactory {public static boolean MOCK = true; public static Object getBean(String id) {if("searchService".equals(id)) {if(MOCK) {return new SearchServiceMock();} else {return getSearchService();}}throw new RuntimeException("cannot find the bean with id :" + id);}public static LuceneDAO getLuceneDAO() {if(MOCK) {return new LuceneDAOMock();} else {throw new RuntimeException("cannot find the LuceneDAO bean");}}public static MysqlDAO getMysqlDAO() {if(MOCK) {return new MysqlDAOMock();} else {throw new RuntimeException("cannot find the MysqlDAO bean");}}public static SearchService getSearchService() {if(MOCK) {return new SearchServiceMock();} else {return new SearchServiceInRealBiz();}}public static void main(String ... arg) {String keywords = "test";SearchService searchService = (SearchService)BeanFactory.getBean("searchService");List results = searchService.search(keywords);for(int i = 0; i < results.size(); i++) {Result result = (Result) results.get(i);System.out.print("[" + result.title + "]");System.out.println(result.content);}}}
測試無誤。bingo!
I gotta get the GREEN BAR!!!(JUnit專用~~~)
現在,我開始發現
BeanFactory
有點兒無處不在了,一旦需要修改,哪怕是改個 BeanFactory 的名字,如果沒有refactor工具,工作也是相當麻煩的。
這就是所謂的 “上帝類 God Class” “上帝對象 God Object”
為了減少影響,至少,我應該儘可能的把 BeanFactory 的字樣從其他類裡面移出去。
讓我們高唱國際歌,“從來就沒有什麼救世主,也沒有神仙皇帝”,對BeanFactory進行大刀闊斧的革命~~!
在我們現在的代碼裡,僅有 SearchServiceInRealBiz 擁抱了 “上帝”。 因此,想想辦法:
public class SearchServiceInRealBiz implements SearchService {private LuceneDAO luceneDAO;private MysqlDAO mysqlDAO;private SearchServiceInRealBiz(LuceneDAO luceneDAO, MysqlDAO mysqlDAO) {super();this.luceneDAO = luceneDAO;this.mysqlDAO = mysqlDAO;}public List search(String keywords) {int[] idlist = luceneDAO.findDocIDs(keywords);List<Result> results = mysqlDAO.getResultsByDocIDs(idlist);return results;}}
當然 BeanFactory 肯定會報錯的,
必須說明:我期待這樣的報錯(編譯時間報錯),這樣我們才能發現對代碼進行調整之後會影響那些地方。然後隨之做出對應調整。
參考另外一種方式:
private LuceneDAO luceneDAO;private MysqlDAO mysqlDAO;public void setLuceneDAO(LuceneDAO luceneDAO) {this.luceneDAO = luceneDAO;}public void setMysqlDAO(MysqlDAO mysqlDAO) {this.mysqlDAO = mysqlDAO;}public List search(String keywords) {int[] idlist = luceneDAO.findDocIDs(keywords);List<Result> results = mysqlDAO.getResultsByDocIDs(idlist);return results;}
這樣eclipse不會報錯,但是。。。。。。。你失去了改正的機會!
混過了編譯時間檢測,跑不掉運行時報錯D,親!而且,這個問題會隱形讓你吐血~~~~
(其實,我在暗示你應該在spring的xml中選擇哪一種裝配方式……)
現在,讓我們修複BeanFactory 的報錯:
public static SearchService getSearchService() {if(MOCK) {return new SearchServiceMock();} else {LuceneDAO luceneDAO = getLuceneDAO();MysqlDAO mysqlDAO = getMysqlDAO();return new SearchServiceInRealBiz(luceneDAO, mysqlDAO);}}
RUN一次,ok,沒有問題。 I LOVE GREE BAR!
《兒歌三百首》絕非浪得虛名~~
to be continued....