摘要: 用Commons Modeler 開(kāi)發(fā)JMX應(yīng)用 Modeler組件是Jakarta Commons 項(xiàng)目針對(duì)Model MBeans提供的一個(gè)便利的開(kāi)發(fā)組件。 首先介紹一下基本的概念:Managed bean簡(jiǎn)稱Mbean,是對(duì)可被管理的資源的抽象定義,ModelBean是JMX定義的Mbean中動(dòng)態(tài)和靈活的一種。但是要實(shí)現(xiàn)它開(kāi)發(fā)人員必須設(shè)置大量的Metadata信息。Modeler組件針對(duì)Mo...
閱讀全文
單元測(cè)試的局限
關(guān)于單元測(cè)試的一篇好文章,我只翻譯了一段,有興趣的可以看看原文。
譯自:http://www.theserverside.com/blogs/showblog.tss?id=Unitized
考慮一下單元測(cè)試的目的和原則:
1。盡量小粒度的“單元”被測(cè)試。
2。測(cè)試在于其它模塊隔離地情況下獨(dú)立地完成。
3。Mocking在隔離的方面作出了強(qiáng)化。
4。代碼和測(cè)試都是同一個(gè)人完成的。
把上面提到的考慮在一起,意味著單元測(cè)試是測(cè)試整個(gè)代碼中最低層次的部分,每一個(gè)部分是和其它部分隔離的。測(cè)試和編碼是同一個(gè)人完成的。
這種方式的測(cè)試正是“我”所說(shuō)的“l(fā)ow hanging fruit”[可輕易實(shí)現(xiàn)的目標(biāo) (easy targets)]。它可以捕獲小的問(wèn)題,也就是可以找到不符合單元測(cè)試的要求的單獨(dú)的函數(shù)或者類。
單元測(cè)試是一個(gè)好事,提供了對(duì)于自己代碼正確性的有價(jià)值的反饋。但是“Keep in mind" 它只能得到“l(fā)ow hanging fruit”。在設(shè)計(jì)上,單元測(cè)試被期望“簡(jiǎn)單的”、“作為系統(tǒng)中獨(dú)立的小部分”。因?yàn)檫@些,在它的本質(zhì)上(與生俱來(lái)的),單元測(cè)試沒(méi)有考慮系統(tǒng)的“組合”,它只考慮獨(dú)立的部分。單元測(cè)試從不檢查一個(gè)系統(tǒng)中內(nèi)部的聯(lián)絡(luò),從不檢查他們是如何捆綁在一起的。
根據(jù)“我”的經(jīng)驗(yàn),系統(tǒng)中如何聯(lián)絡(luò)和如何捆綁在一起正是系統(tǒng)的復(fù)雜度所在。
正是這種“連接”定義了你的設(shè)計(jì),如果在一個(gè)足夠高的層次上考慮,這種聯(lián)系甚至可以理解為系統(tǒng)的架構(gòu)。
信息是如何在不同的軟件層上和不同的組件之間的流動(dòng)實(shí)實(shí)在在的定義了一個(gè)應(yīng)用。
由此看來(lái),單元測(cè)試是不會(huì)測(cè)試一個(gè)應(yīng)用的這些方面的。單元測(cè)試忽略了信息是如何在不同的層和不同的組件之間流動(dòng)的,忽略了類和對(duì)象在一個(gè)大的架構(gòu)和設(shè)計(jì)中如何相互關(guān)聯(lián)和組合在一起的。
這就意味著單元測(cè)試只能在獨(dú)立的代碼片斷中捕獲簡(jiǎn)單的錯(cuò)誤,但是對(duì)系統(tǒng)的整體的設(shè)計(jì)或者機(jī)構(gòu)Say nothing。
設(shè)計(jì)和架構(gòu)限定了你的系統(tǒng)的性能,內(nèi)存使用,“端到端”的正確性。[用戶的輸入,到Server處理所使用的,到最后的返回的整個(gè)路徑]。所以這些是如何進(jìn)行聯(lián)系的顯示了系統(tǒng)的行為,并且正是在這個(gè)范圍中存在著the toughest bug 和 problems,要想讓一切OK,程序員們也要在這個(gè)地方苦干不止。
編寫(xiě)隔離的獨(dú)立組件是容易的,把他們粘合在一起是困難的。單元測(cè)試只在隔離的在獨(dú)立的部分上作判斷,而不是在整體上。
使系統(tǒng)中的一個(gè)組件的動(dòng)作正確相當(dāng)來(lái)說(shuō)是價(jià)值不高的活動(dòng)。獨(dú)立的編寫(xiě)一個(gè)系統(tǒng)的組件不是計(jì)算機(jī)編程的困難的部分,任何一個(gè)個(gè)體的小的組件都是容易編寫(xiě)的。在開(kāi)發(fā)中最難的部分來(lái)自于如何把所有的組件捆綁在一起工作。單元測(cè)試可以驗(yàn)證每一個(gè)你編寫(xiě)的獨(dú)立組件是不是按照你所想的那樣工作,但是它不能檢查更復(fù)雜的“wiring”--“wiring”正是我們的設(shè)計(jì),開(kāi)發(fā)和調(diào)試絕大部分工數(shù)所在。
從上看來(lái),單元測(cè)試不會(huì)指出“端到端”的處理是否正常,不會(huì)關(guān)心性能,不會(huì)關(guān)心內(nèi)存的使用,不會(huì)關(guān)心可用性,不會(huì)關(guān)心代碼是否正是用戶想要的。它也不會(huì)捕獲多線程的bug,或者錯(cuò)誤的理解了外部API或者子系統(tǒng)的使用等等。這些并不意味著單元測(cè)試是不好的或者應(yīng)該避免的,它只是說(shuō)明單元測(cè)試只會(huì)給你一個(gè)有限的回報(bào)。設(shè)想我們作為開(kāi)發(fā)人員,我們沒(méi)有無(wú)限的資源去開(kāi)發(fā)我們的代碼,我們不得不聰明的決定我們要把我們的精力放在那里。我們不得不經(jīng)常的折衷和決定怎樣做有最好的效果。
在“我”參加的所有開(kāi)發(fā)中。單元測(cè)試覆蓋了絕大部分的代碼,但是在以下的方面的努力還差得很遠(yuǎn):
1。應(yīng)用程序設(shè)計(jì)的本身。你應(yīng)該花費(fèi)更多的時(shí)間在你的設(shè)計(jì)上,采用一種迭代的,真實(shí)地方式而不是花費(fèi)在單元測(cè)試上,因?yàn)橐粋€(gè)好的設(shè)計(jì)得到的回報(bào)比任何數(shù)量的單元測(cè)試都多。
2。集成測(cè)試(Integration Test)。集成測(cè)試的測(cè)試特征是基于“端到端”的。通過(guò)它的設(shè)計(jì)可以證明你的獨(dú)立的組件可以工作在一起。通過(guò)一個(gè)集成測(cè)試,你可以更信賴你的系統(tǒng)按照“端到端”的方式工作,而不是一些獨(dú)立的對(duì)象。
3。功能測(cè)試和回歸測(cè)試(Functional Test&Regression Test)。系統(tǒng)不是開(kāi)發(fā)人員想的那樣,而是用戶期望它是什么樣子。更進(jìn)一步,回歸測(cè)試當(dāng)新的特性被追加或者底層的代碼被改變時(shí),驗(yàn)證高級(jí)別的功能的正確性沒(méi)有被改變。
4。非功能測(cè)試(Non-function Test)。在可接受的運(yùn)行需求下,代碼作為整體被運(yùn)行,請(qǐng)求在可接受的時(shí)間范圍內(nèi)被處理。sever不會(huì)因?yàn)橛?個(gè)用戶請(qǐng)求就會(huì)memory緊張。等等。
做以上的東西會(huì)比單元測(cè)試難很多。但是在同樣的投入下會(huì)得到更多的回報(bào)。
Java ClassLoader 實(shí)現(xiàn)程序的擴(kuò)展性
今天在完成一個(gè)功能的時(shí)候,使用了ServiceLocate模式,
通過(guò)這個(gè)模式,在程序中可以自由的加載其他成員實(shí)現(xiàn)的功能模塊
具體的做法:
1)定義標(biāo)準(zhǔn)的服務(wù)接口。
2)定義描述實(shí)現(xiàn)服務(wù)接口的xml文件。
3)程序讀取該xml文件,使用Class.newInstance()實(shí)例化具體的服務(wù)對(duì)象。
4)建立一個(gè)特定服務(wù)和特定服務(wù)實(shí)現(xiàn)的對(duì)應(yīng)的HashMap對(duì)象。完成注冊(cè)任務(wù)。
5)主程序中根據(jù)具體的服務(wù)從HashMap中取得具體的對(duì)象進(jìn)行服務(wù)。
這個(gè)方法還不錯(cuò),可以完成基于Interface的開(kāi)發(fā)要求,利于Test和程序的拓展性。
有新的要求出現(xiàn)后,只需要添加xml中的元素和具體的實(shí)現(xiàn)類就可以了。
接下來(lái),繼續(xù)想。又發(fā)現(xiàn)了一些問(wèn)題:
1)xml是和程序一起發(fā)布的,如果用戶隨意改動(dòng)了。很明顯程序會(huì)崩潰。
解決方法:xml放在jar包中,使用getClass().getResourceAsStream(String name)
自己加載進(jìn)來(lái)。用戶完全不知道具體的情況。
2)如果把xml放在了jar包中“藏起來(lái)”,實(shí)際上原來(lái)帶來(lái)的動(dòng)態(tài)擴(kuò)展的特性,
也就沒(méi)有那么明顯了。如何解決呢
細(xì)細(xì)想來(lái),這個(gè)問(wèn)題的關(guān)鍵在于,所有的服務(wù)實(shí)例對(duì)象的創(chuàng)建和注冊(cè)都是在
主控程序中通過(guò)xml來(lái)完成的。如果可以把這個(gè)注冊(cè)和實(shí)例化的過(guò)程從主控程序
中分離出來(lái),通過(guò)每個(gè)服務(wù)實(shí)例對(duì)象自動(dòng)注冊(cè)來(lái)完成,那么才算是真正的可拓展的。
如果需要完成新的功能,只需要把新的服務(wù)對(duì)象Class發(fā)布,重新運(yùn)行主控程序就會(huì)實(shí)現(xiàn)新的功能。(看起來(lái)就和Eclipse一樣)
這真是一個(gè)不錯(cuò)的想法,但是怎么做呢?
看看Eclipse如何做的。
http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html
首先要有一個(gè)規(guī)定的plugin deploy目錄,這樣主程序才知道從哪里加載。
要有一個(gè)plugin.xml文件描述這個(gè)plugin.這著文件中有屬性:class="foo.bar.Plugin">
看上去和我們原來(lái)做的方式一樣啊。但是它是如何把這個(gè)目錄下的plugin都加載的呢?
(我沒(méi)有看過(guò)Eclipse的源碼,不知道他是怎么寫(xiě)的)
再想想,其實(shí)主要要解決的問(wèn)題是不通過(guò)主框架程序注冊(cè)服務(wù)實(shí)現(xiàn),
應(yīng)該由服務(wù)程序自己注冊(cè)上來(lái)。按照這個(gè)思路,我想有兩種解決方案。
1)服務(wù)接口添加registerService 方法。
* 在jar的METATINFO文件中定義類名。
* 從特定的目錄中讀取jar/class文件。
* 通過(guò)URLClassLoader.newInstance()加載
* 加載后把ServiceLoader作為參數(shù)出入 service.registerSevice()中
* service對(duì)象完成自己的注冊(cè)。
2)服務(wù)添加static代碼端在類被實(shí)例化的時(shí)候自動(dòng)完成注冊(cè)。
* 加載之前和上一個(gè)方法一樣。
* 對(duì)SeviceLoader對(duì)象實(shí)現(xiàn)為單態(tài)的模式。提供靜態(tài)的注冊(cè)方法。
* 在servie對(duì)象中實(shí)現(xiàn)如下的代碼段完成自動(dòng)注冊(cè)。
static
{
ServiceLoader.registerService(new service());
}
這樣看來(lái)總算OK了吧。
原來(lái)在http://a-ke.blogbus.com 發(fā)現(xiàn)太慢了。
自己上都費(fèi)勁。轉(zhuǎn)到這里會(huì)好一些吧!
回頭把東西都搬過(guò)來(lái)。