1. 程式人生 > >開發,從需求出發 · 之四 春天在這裏

開發,從需求出發 · 之四 春天在這裏

ins 大型 features 讓我 查看 width package 代碼 tle

首先,我要大字標語表達立場:

你所使用的framework & non-core features,就跟女人穿在身上的衣服一樣,越少越好!

技術分享


扯淡完成,說正經的。


讓我們繼續盯著花姐——啊,不——是 BeanFactory看。

	public static SearchService getSearchService() {
		if(MOCK) {
			return new SearchServiceMock();
		} else {
			LuceneDAO luceneDAO = getLuceneDAO();
			MysqlDAO mysqlDAO = getMysqlDAO();
			
			return new SearchServiceInRealBiz(luceneDAO, mysqlDAO);
		}
	}

有木有點兒標題所說的“春天在這裏”的意思了?

納尼?沒看出來?

好吧,或許你習慣了spring的xml裝配方式,所以認為把兩者關聯起來看實在須要超常的想象力,那麽,

我把BeanFactory改頭換面。簡單的實現一個基於文本字符串的裝配,咋們再來看看效果:


package cn.com.sitefromscrath;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BeanFactory {
	
	private static Map<String, String> appBean = new HashMap<String, String>();
	private static Map<String, String[]> appRef = new HashMap<String, String[]>();
	
	static {
		appBean.put("luceneDAO", "cn.com.sitefromscrath.dao.LuceneDAOMock");
		appBean.put("mysqlDAO", "cn.com.sitefromscrath.dao.MysqlDAOMock");
		appBean.put("searchService", "cn.com.sitefromscrath.service.SearchServiceInRealBiz");
		
		appRef.put("searchService", new String[]{"luceneDAO", "mysqlDAO"});
	}
		
	public static Object getBean(String id) {
		
		try {
		
			String className = appBean.get(id);		
			Class clazz = Class.forName(className);
			Constructor constructor;
			
			String[] ref = appRef.get(id);
			
			if(ref == null || ref.length == 0) {			
				constructor = clazz.getConstructor();
				return (Object)constructor.newInstance();
			}
			
			Class[] parameterTypes = new Class[ref.length];
			Object[] initargs = new Object[ref.length];
			
			for(int i = 0; i < ref.length; i++) {
				String r = ref[i];
				
				String rclassName = appBean.get(r);
				parameterTypes[i] = Class.forName(rclassName).getInterfaces()[0]; //這裏我偷懶了:)
				initargs[i] = getBean(r);
			}
			
			constructor = clazz.getConstructor(parameterTypes);
			
			return (Object)constructor.newInstance(initargs);
			
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}		
	}
	
	public static void main(String ... arg) {
		
		LuceneDAO luceneDAO = (LuceneDAO) getBean("luceneDAO");
		int[] vals = luceneDAO.findDocIDs("test");
		for(int v : vals) {
			System.out.println(v);
		}
		
		String keywords = "test";
		SearchService searchService = (SearchService)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);
		}
	}

}

執行結果輸出:

1
2
3
4
[result 1]something..................
[result 2]something..................
[result 3]something..................
[result 4]something..................

結果正確!


再看看我們對類的裝配:

	private static Map<String, String> appBean = new HashMap<String, String>();
	private static Map<String, String[]> appRef = new HashMap<String, String[]>();
	
	static {
		appBean.put("luceneDAO", "cn.com.sitefromscrath.dao.LuceneDAOMock");
		appBean.put("mysqlDAO", "cn.com.sitefromscrath.dao.MysqlDAOMock");
		appBean.put("searchService", "cn.com.sitefromscrath.service.SearchServiceInRealBiz");
		
		appRef.put("searchService", new String[]{"luceneDAO", "mysqlDAO"});
	}

請比較和spring.xml的差別:

<?xml version="1.0" encoding="UTF-8"?>
<beans >

	<bean id="luceneDAO" class="cn.com.sitefromscrath.dao.LuceneDAOMock" />
	
	<bean id="mysqlDAO" class="cn.com.sitefromscrath.dao.MysqlDAOMock" /> 
	
	<bean id="searchService" class="cn.com.sitefromscrath.service.SearchServiceInRealBiz">
		<constructor-arg index="1" ref="luceneDAO" />
		<constructor-arg index="2" ref="mysqlDAO" />
	</bean>  

</beans>

好吧,沒什麽根本上的差別,我們相同可以解析xml,得到我們自己實現的BeanFactory所須要的一切要素。

好了。經過我們將代碼從零開始,重復重構,到一個比較“經典”的模式。我們找到了“春天”。這也是spring的core features。


或許是對spring看待的角度不同,我發現我對spring的依賴註入和控制反轉的用途和不少人並不一致。下一章。我打算結合一些開發和測試技巧,論述一下我的看法。

只是。如今,是吐槽spring的時間:

我在我的博文《thinking in asp》appendix A - 吐槽JAVA 中以前說到:

還有新版本號的spring,怎麽說呢,它把java的annotation機制玩兒到了“奇技淫巧”的程度。

因為那個系列主要是講視頻編轉碼技術。因此。對spring僅僅是一帶而過,如今總算找到機會了:)


Long long ago, in the old good times, spring還是依賴xml做裝配的小姑娘,青春漂亮,帶著點兒書卷學生氣。

可是如今看,這丫頭已經塗脂抹粉,躋身上流社會。出入商務場合。盡管不討厭,可是不讓人認為親近了 -_-b


看傳統的 spring.xml,就如同看電路板設計圖,一個個元器件清清楚楚,什麽型號,怎麽走線。怎樣裝配。盡管沒有圖形化。可是一目了然,基本上能夠做到不用查看源代碼。就能把整個系統的邏輯關系梳理的八九不離十。

而自從有了annotation,比方autowire。xml不重要了,你無法再從一個基於spring的大型項目中迅速找到一份天然的“提綱”。

——spring開始不顧一切的媚俗。——或許俺是個受虐狂,只是“ruby way”和“傲嬌”的python顯然更對我的胃口——設計原則是不能夠松口的:)

我以前問spring的一些使用者,怎樣找到通過annotation裝配尤其是自己主動裝配的類,或者是某個隱藏在spring-mvc框架下annotation聲明的URL,

給我的答案例如以下:

首先:

技術分享

然後:

技術分享


好吧。我得到的結論是:與其如此,不如不用:)

當然,假設你說。通過一些詞法/語法解析器,也能夠得到基於annotation的“提綱”。比方用 lex+yacc 亦或 antlr 打造一個工具。

本座的答復是:老子被編譯原理搞的幾宿沒睡了,小心一指頭把你戳出去三公裏遠去~~~!

技術分享技術分享技術分享


接著。說說spring框架下怎樣強測試的問題:

時刻記住。每個模塊。甚至最“小”的方法,在實現它之前,都必需要先設計怎樣測試它。


由於我們如今討論的是一個web項目,我想說一個非常多開發人員會使用的方法:

ContextListener --> WebApplication --> BeanFactory

因為spring的“人性化”。這個步驟甚至不須要你寫代碼。

如今的問題是,假設我們的項目採取這種方式。你怎樣做dao 或者 service層的單元測試?

比方前面提到的 SearchService,他須要通過spring裝配LuceneDAO 和 MysqlDAO,可是問題出現了:

你假設想讓SearchService的方法跑起來,你必須啟動TOMCAT等web容器!

這樣的緊耦合的程度簡直是令人發指 :) 讓一切單元測試成為不可能~~~~

而我,在前面的章節重復強調 “不須要啟動tomcat。不須要查看網頁的實際效果,也能保證系統模塊的正確” 就是這個意思:)


當然。spring提供了ApplicationContext,在web容器中相同能用——這也是我使用的方式——可是,我想說的是。spring的WebApplication模式錯誤的誘導了開發人員。引發了大量的 bad smell。


在玩兒了一把怎樣從最簡單的需求出發。重復重構到模式/框架之後,下一章我會再次繞回去,spring已經說得夠多的啦:)

to be continued.....

開發,從需求出發 &#183; 之四 春天在這裏