<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    GHawk

    2005年12月10日 #

    建議方正要是勝訴的話,為開源事業(yè)做點(diǎn)貢獻(xiàn)

    中國(guó)人民可以說是被盜版和Windows慣壞了。在Linux默認(rèn)環(huán)境下的中文顯示至今慘不忍睹。
    看看現(xiàn)在一些主要的發(fā)行版本,默認(rèn)設(shè)置下的日語的顯示已經(jīng)相當(dāng)不錯(cuò)了。
    作為漢字發(fā)祥地且擁有眾多人口的中國(guó),實(shí)在是有些悲哀。
    文泉驛計(jì)劃正在為實(shí)現(xiàn)這個(gè)目標(biāo)努力。作為一個(gè)非盈利性組織,他們的貢獻(xiàn)的確值得贊賞。
    最近方正正在為字體的事情打官司,并提出了高額賠償?shù)囊蟆=ㄗh方正要是能夠勝訴的話,貢獻(xiàn)一些字體給開源組織,為弘揚(yáng)漢語言文化多做點(diǎn)貢獻(xiàn)。

    posted @ 2007-08-23 15:01 GHawk 閱讀(651) | 評(píng)論 (1)編輯 收藏

    Maven2 用于度量和品質(zhì)保證的插件


    以上是一些常用的用于品質(zhì)管理的插件。默認(rèn)情況下都不用配置,相當(dāng)方便。如果需要手動(dòng)配置的話,根據(jù)網(wǎng)上的文檔也相當(dāng)容易配置。
    apache的maven plugin頁(yè)面: http://maven.apache.org/plugins/
    codehaus mojo 頁(yè)面: http://mojo.codehaus.org

    posted @ 2007-05-28 14:32 GHawk 閱讀(1560) | 評(píng)論 (1)編輯 收藏

    Scripting in Mustang 的一點(diǎn)啟發(fā)

    2006 Sun Techdays Shanghai 的第2天下午有一個(gè)名為《Java Scripting: One VM, Many Languages》的Session。

    Rags為大家展示了Mustang的一個(gè)新特性,Scripting in Java——腳本語言支持。

    通過加入腳本引擎的支持,就能夠在Java中解釋Javascript,python,ruby等諸多腳本語言。

    對(duì)于這個(gè)特性,想到的一個(gè)可能的應(yīng)用就是在annotation中寫腳本語言,然后在代碼中用相應(yīng)的腳本語言引擎解釋執(zhí)行。
    保留到運(yùn)行時(shí)的annotation可以用實(shí)現(xiàn)aop的功能,使用非inline的腳本就可以更靈活地控制aspect的行為。

    比如:
    //inline scripting
    @ScriptBefore(script
    ="",language="javascript"?)
    public?void?foo()?{
    ???
    }

    //non-inline scripting
    @ScriptBefore(file
    ="scripts/logging.js",language="javascript")
    public?void?bar()?{

    }

    posted @ 2006-09-26 10:04 GHawk 閱讀(1327) | 評(píng)論 (3)編輯 收藏

    一個(gè)XPer提供的一些經(jīng)驗(yàn)

    前些天,和一位XPer進(jìn)行了一次愉快的談話。他向我講述了一些感覺很有效的實(shí)踐。

    關(guān)于過程和迭代
    他曾經(jīng)參與過的項(xiàng)目的迭代是以月為迭代單位的,但事實(shí)上每周都會(huì)重復(fù)一個(gè)簡(jiǎn)單的過程。
    在迭代過程中,他非常推崇Burn-Down Charts。這是一個(gè)Scrum的工具。通過Burn-Down Charts,能夠把過程中間的變化記錄下來,使過程高度可視化。等到一次迭代完成,回顧一下所有的Burn-Down Charts就能作為改進(jìn)的判斷依據(jù)。
    KPT Meeting。所謂KPT Meeting就是 Keep-Prevent-Try metting。小組定期舉行KPT會(huì)議(基本上是每周一次)。在KTP會(huì)議上,通過頭腦風(fēng)暴的方式每個(gè)人(不是某幾個(gè)人)把各自認(rèn)為前一階段里做得好的方面寫在Keep一欄里;做得不好的方面寫在Prevent一欄里;希望嘗試的寫在Try一欄里。然后大家對(duì)這些項(xiàng)目進(jìn)行評(píng)估和篩選。下一階段中,Keep的項(xiàng)目繼續(xù)保持,Prevent的項(xiàng)目應(yīng)該杜絕,Try的項(xiàng)目進(jìn)行嘗試。

    工具
    在開展這些實(shí)踐的時(shí)候,交流比較頻繁。首推的工具是Mini white boardDC
    選擇Mini white board的原因并不是因?yàn)閹в?mini"聽上去會(huì)像 Mini Cooper 或者 iPod mini 那么cool。因?yàn)橐粔KA3左右大小的白板非常適合個(gè)人或者結(jié)對(duì)使用,而且環(huán)保(省去了草稿紙)。雖然整個(gè)團(tuán)隊(duì)也有用于大規(guī)模交流的更大的白板,但那屬于“競(jìng)爭(zhēng)資源”,各自使用自己的白板更為方便。
    交流結(jié)果產(chǎn)生后,為了不花不必要的時(shí)間去做精美的文檔,一臺(tái)輕便的DC往往是最合適的選擇。當(dāng)然,如果足夠,手機(jī)上的照相功能也可以完成同樣的任務(wù)。相比偷拍街上的MM,這些電子產(chǎn)品能夠?qū)崿F(xiàn)更大的價(jià)值。

    關(guān)于結(jié)對(duì)
    每天進(jìn)行6小時(shí)的結(jié)對(duì)編程,分3次,每次2小時(shí)。每次和不同的成員組隊(duì)。在結(jié)隊(duì)的時(shí)候充分利用了上面提到的工具進(jìn)行交流。如果出現(xiàn)兩個(gè)人不能解決的問題的時(shí)候,會(huì)立即向整個(gè)團(tuán)隊(duì)提出,這樣可能導(dǎo)致一次stand-up meeting。即使問題不能馬上解決,至少也能確保每個(gè)人都知道這個(gè)問題。

    posted @ 2006-08-24 15:45 GHawk 閱讀(1445) | 評(píng)論 (0)編輯 收藏

    關(guān)于locale的設(shè)定 (轉(zhuǎn))

    轉(zhuǎn)自:http://www.syxin.com/2006/03/localelocale.html

    關(guān)于locale的設(shè)定
    ?
    locale是國(guó)際化與本土化過程中的一個(gè)非常重要的概念,個(gè)人認(rèn)為,對(duì)于中文用戶來說,通常會(huì)涉及到的國(guó)際化或者本土化,大致包含三個(gè)方面:看中文,寫中文,與window中文系統(tǒng)的兼容和通信。從實(shí)際經(jīng)驗(yàn)上看來,locale的設(shè)定與看中文關(guān)系不大,但是與寫中文,及window分區(qū)的掛載方式有很密切的關(guān)系。本人認(rèn)為就像一個(gè)純英文的Windows能夠?yàn)g覽中文,日文或者意大利文網(wǎng)頁(yè)一樣,你不需要設(shè)定locale就可以看中文。那么,為什么要設(shè)定locale呢?什么時(shí)候會(huì)用到locale呢?

    一、為什么要設(shè)定locale
    正如前面我所講的,設(shè)定locale與你能否瀏覽中文的網(wǎng)頁(yè)沒有直接的關(guān)系,即便你把locale設(shè)置成en_US.ISO-8859-1這樣一個(gè)標(biāo)準(zhǔn)的英文locale你照樣可以瀏覽中文的網(wǎng)頁(yè),只要你的系統(tǒng)里面有相應(yīng)的字符集(這個(gè)都不一定需要)和合適的字體(如simsun),瀏覽器就可以把網(wǎng)頁(yè)翻譯成中文給你看。具體的過程是網(wǎng)絡(luò)把網(wǎng)頁(yè)傳送到你的機(jī)器上之后,瀏覽器會(huì)判斷相應(yīng)的編碼的字符集,根據(jù)網(wǎng)頁(yè)采用的字符集,去字體庫(kù)里面找合適的字體,然后由文字渲染工具把相應(yīng)的文字在屏幕上顯示出來。

    ?
    在下文本人會(huì)偶爾把字符集比喻成密碼本,個(gè)人覺得對(duì)于一些東西比較容易理解,假如你不習(xí)慣的話,把全文copy到任何文本編輯器,用字符集替換密碼本即可。
    ?
    那有時(shí)候網(wǎng)頁(yè)顯示亂碼或者都是方框是怎么回事呢?個(gè)人認(rèn)為,顯示亂碼是因?yàn)樵O(shè)定的字符集不對(duì)(或者沒有相應(yīng)的字符集),例如網(wǎng)頁(yè)是用UTF-8編碼的,你非要用GB2312去看,而系統(tǒng)根據(jù)GB2312去找字體,然后在屏幕上顯示,當(dāng)然是一堆的亂碼,也就是說你用一個(gè)錯(cuò)誤的密碼本去翻譯發(fā)給你的電報(bào),當(dāng)然內(nèi)容那叫一個(gè)亂;至于有些時(shí)候?yàn)g覽的網(wǎng)頁(yè)能顯示一部分漢字,但有很多的地方是方框,能夠顯示漢字說明瀏覽器已經(jīng)正確的判斷出了網(wǎng)頁(yè)的編碼,并在字體庫(kù)里面找到了相應(yīng)的文字,但是并不是每個(gè)字體庫(kù)都包含某個(gè)字符集全部的字體的緣故,有些時(shí)候會(huì)顯示不完全,找一個(gè)比較全的支持較多字符集的字體就可以了。
    ?

    既然我能夠?yàn)g覽中文網(wǎng)頁(yè),那為什么我還要設(shè)定locale呢?
    ?
    其實(shí)你有沒有想過這么一個(gè)問題,為什么gentoo官方論壇上中文論壇的網(wǎng)頁(yè)是用UTF-8編碼的(雖然大家一直強(qiáng)烈建議用GB2312編碼),但是新浪網(wǎng)就是用GB2312編碼的呢?而Xorg的官方網(wǎng)頁(yè)竟然是ISO-8859-15編碼的,我沒有設(shè)定這個(gè)locale怎么一樣的能瀏覽呢?這個(gè)問題就像是你有所有的密碼本,不論某個(gè)網(wǎng)站是用什么字符集編碼的,你都可以用你手里的密碼本把他們翻譯過來,但問題是雖然你能瀏覽中文網(wǎng)頁(yè),但是在整個(gè)操作系統(tǒng)里面流動(dòng)的還是英文字符。所以,就像你能聽懂英語,也能聽懂中文。
    最根本的問題是:你不可以寫中文。
    ?
    當(dāng)你決定要寫什么東西的時(shí)候,首先要決定的一件事情是用那種語言,對(duì)于計(jì)算機(jī)來說就是你要是用哪一種字符集,你就必須告訴你的linux系統(tǒng),你想用那一本密碼本去寫你想要寫的東西。知道為什么需要用GB2312字符集去瀏覽新浪了吧,因?yàn)樾吕说木W(wǎng)頁(yè)是用GB2312寫的。
    ?
    為了讓你的Linux能夠輸入中文,就需要把系統(tǒng)的locale設(shè)定成中文的(嚴(yán)格說來是locale中的語言類別LC_CTYPE ),例如zh_CN.GB2312、zh_CN.GB18030或者zh_CN.UTF-8。很多人都不明白這些古里古怪的表達(dá)方式。這個(gè)外星表達(dá)式規(guī)定了什么東西呢?這個(gè)問題稍后詳述,現(xiàn)在只需要知道,這是locale的表達(dá)方式就可以了。
    ?
    二、到底什么是locale?
    locale這個(gè)單詞中文翻譯成地區(qū)或者地域,其實(shí)這個(gè)單詞包含的意義要寬泛很多。Locale是根據(jù)計(jì)算機(jī)用戶所使用的語言,所在國(guó)家或者地區(qū),以及當(dāng)?shù)氐奈幕瘋鹘y(tǒng)所定義的一個(gè)軟件運(yùn)行時(shí)的語言環(huán)境。
    ?
    這個(gè)用戶環(huán)境可以按照所涉及到的文化傳統(tǒng)的各個(gè)方面分成幾個(gè)大類,通常包括用戶所使用的語言符號(hào)及其分類(LC_CTYPE),數(shù)字(LC_NUMERIC),比較和排序習(xí)慣(LC_COLLATE),時(shí)間顯示格式(LC_TIME),貨幣單位(LC_MONETARY),信息主要是提示信息,錯(cuò)誤信息, 狀態(tài)信息, 標(biāo)題, 標(biāo)簽, 按鈕和菜單等(LC_MESSAGES),姓名書寫方式(LC_NAME),地址書寫方式(LC_ADDRESS),電話號(hào)碼書寫方式(LC_TELEPHONE),度量衡表達(dá)方式(LC_MEASUREMENT),默認(rèn)紙張尺寸大小(LC_PAPER)和locale對(duì)自身包含信息的概述(LC_IDENTIFICATION)。
    ?
    所以說,locale就是某一個(gè)地域內(nèi)的人們的語言習(xí)慣和文化傳統(tǒng)和生活習(xí)慣。一個(gè)地區(qū)的locale就是根據(jù)這幾大類的習(xí)慣定義的,這些locale定義文件放在/usr/share/i18n/locales目錄下面,例如en_US, zh_CN and de_DE@euro都是locale的定義文件,這些文件都是用文本格式書寫的,你可以用寫字板打開,看看里邊的內(nèi)容,當(dāng)然出了有限的注釋以外,大部分東西可能你都看不懂,因?yàn)槭怯玫腢nicode的字符索引方式。
    ?
    對(duì)于de_DE@euro的一點(diǎn)說明,@后邊是修正項(xiàng),也就是說你可以看到兩個(gè)德國(guó)的locale:
    /usr/share/i18n/locales/de_DE@euro
    /usr/share/i18n/locales/de_DE
    打開這兩個(gè)locale定義,你就會(huì)知道它們的差別在于de_DE@euro使用的是歐洲的排序、比較和縮進(jìn)習(xí)慣,而de_DE用的是德國(guó)的標(biāo)準(zhǔn)習(xí)慣。
    ?
    上面我們說到了zh_CN.GB18030的前半部分,后半部分是什么呢?大部分Linux用戶都知道是系統(tǒng)采用的字符集。
    ?
    三、什么是字符集?
    字符集就是字符,尤其是非英語字符在系統(tǒng)內(nèi)的編碼方式,也就是通常所說的內(nèi)碼,所有的字符集都放在/usr/share/i18n/charmaps,所有的字符集也都是用Unicode編號(hào)索引的。Unicode用統(tǒng)一的編號(hào)來索引目前已知的全部的符號(hào)。而字符集則是這些符號(hào)的編碼方式,或者說是在網(wǎng)絡(luò)傳輸,計(jì)算機(jī)內(nèi)部通信的時(shí)候,對(duì)于不同字符的表達(dá)方式,Unicode是一個(gè)靜態(tài)的概念,字符集是一個(gè)動(dòng)態(tài)的概念,是每一個(gè)字符傳遞或傳輸?shù)木唧w形式。就像Unicode編號(hào)U59D0是代表姐姐的“姐”字,但是具體的這個(gè)字是用兩個(gè)字節(jié)表示,三個(gè)字節(jié),還是四個(gè)字節(jié)表示,是字符集的問題。例如:UTF-8字符集就是目前流行的對(duì)字符的編碼方式,UTF-8用一個(gè)字節(jié)表示常用的拉丁字母,用兩個(gè)字節(jié)表示常用的符號(hào),包括常用的中文字符,用三個(gè)表示不常用的字符,用四個(gè)字節(jié)表示其他的古靈精怪的字符。而GB2312字符集就是用兩個(gè)字節(jié)表示所有的字符。需要提到一點(diǎn)的是Unicode除了用編號(hào)索引全部字符以外,本身是用四個(gè)字節(jié)存儲(chǔ)全部字符,這一點(diǎn)在談到掛載windows分區(qū)的時(shí)候是非常重要的一個(gè)概念。所以說你也可以把Unicode看作是一種字符集(我不知道它和UTF-32的關(guān)系,反正UTF-32就是用四個(gè)字節(jié)表示所有的字符的),但是這樣表述符號(hào)是非常浪費(fèi)資源的,因?yàn)樵谟?jì)算機(jī)世界絕大部分時(shí)候用到的是一個(gè)字節(jié)就可以搞定的26個(gè)字母而已。所以才會(huì)有UTF-8,UTF-16等等,要不然大同世界多好,省了這許多麻煩。
    ?

    四、zh_CN.GB2312到底是在說什么?
    Locale 是軟件在運(yùn)行時(shí)的語言環(huán)境, 它包括語言(Language), 地域 (Territory) 和字符集(Codeset)。一個(gè)locale的書寫格式為: 語言[_地域[.字符集]]. 所以說呢,locale總是和一定的字符集相聯(lián)系的。下面舉幾個(gè)例子:
    ?
    1、我說中文,身處中華人民共和國(guó),使用國(guó)標(biāo)2312字符集來表達(dá)字符。
    zh_CN.GB2312=中文_中華人民共和國(guó)+國(guó)標(biāo)2312字符集。
    ?
    2、我說中文,身處中華人民共和國(guó),使用國(guó)標(biāo)18030字符集來表達(dá)字符。
    zh_CN.GB18030=中文_中華人民共和國(guó)+國(guó)標(biāo)18030字符集。
    ?
    3、我說中文,身處中華人民共和國(guó)臺(tái)灣省,使用國(guó)標(biāo)Big5字符集來表達(dá)字符。
    zh_TW.BIG5=中文_臺(tái)灣.大五碼字符集
    ?
    4、我說英文,身處大不列顛,使用ISO-8859-1字符集來表達(dá)字符。
    en_GB.ISO-8859-1=英文_大不列顛.ISO-8859-1字符集
    ?
    5、我說德語,身處德國(guó),使用UTF-8字符集,習(xí)慣了歐洲風(fēng)格。
    de_DE.UTF-8@euro=德語_德國(guó).UTF-8字符集@按照歐洲習(xí)慣加以修正
    ?
    注意不是de_DE@euro.UTF-8,所以完全的locale表達(dá)方式是
    [語言[_地域][.字符集] [@修正值]
    ?
    生成的locale放在/usr/lib/locale/目錄中,并且每個(gè)locale都對(duì)應(yīng)一個(gè)文件夾,也就是說創(chuàng)建了de_DE@euro.UTF-8 locale之后,就生成/usr/lib/locale/de_DE@euro.UTF-8/目錄,里面是具體的每個(gè)locale的內(nèi)容。
    ?
    五、怎樣去自定義locale
    在gentoo生成locale還是很容易的,首先要在USE里面加入userlocales支持,然后編輯locales.build文件,這個(gè)文件用來指示glibc生成locale文件。
    很多人不明白每一個(gè)條目是什么意思。 其實(shí)根據(jù)上面的說明現(xiàn)在應(yīng)該很明確了。
    ?
    File: /etc/locales.build
    en_US/ISO-8859-1
    en_US.UTF-8/UTF-8
    ?
    zh_CN/GB18030
    zh_CN.GBK/GBK
    zh_CN.GB2312/GB2312
    zh_CN.UTF-8/UTF-8
    ?
    上面是我的locales.build文件,依次的說明是這樣的:
    ?
    en_US/ISO-8859-1:生成名為en_US的locale,采用ISO-8859-1字符集,并且把這個(gè)locale作為英文_美國(guó)locale類的默認(rèn)值,其實(shí)它和en_US.ISO-8859-1/ISO-8859-1沒有任何區(qū)別。
    ?
    en_US.UTF-8/UTF-8:生成名為en_US.UTF-8的locale,采用UTF-8字符集。
    ?
    zh_CN/GB18030:生成名為zh_CN的locale,采用GB18030字符集,并且把這個(gè)locale作為中文_中國(guó)locale類的默認(rèn)值,其實(shí)它和zh_CN.GB18030/GB18030沒有任何區(qū)別。
    ?
    zh_CN.GBK/GBK:生成名為zh_CN.GBK的locale,采用GBK字符集。
    zh_CN.GB2312/GB2312:生成名為zh_CN.GB2312的locale,采用GB2312字符集。
    zh_CN.UTF-8/UTF-8:生成名為zh_CN.UTF-8的locale,采用UTF-8字符集。
    ?
    關(guān)于默認(rèn)locale,默認(rèn)locale可以簡(jiǎn)寫成en_US或者zh_CN的形式,只是為了表達(dá)簡(jiǎn)單而已沒有特別的意義。
    ?
    Gentoo在locale定義的時(shí)候掩蓋了一些東西,也就是locale的生成工具:localedef。
    在編譯完glibc之后你可以用這個(gè)localedef 再補(bǔ)充一些locale,就會(huì)更加理解locale了。具體的可以看 localedef 的manpage。
    ?
    $localedef -f 字符集 -i locale定義文件 生成的locale的名稱
    例如
    $localedef -f UTF-8 -i zh_CN zh_CN.UTF-8
    ?
    上面的定義方法和在locales.build中設(shè)定zh_CN.UTF-8/UTF-8的結(jié)果是一樣一樣的。
    ?

    六、locale的五臟六腑
    ?
    剛剛生成了幾個(gè)locale,但是為了讓它們生效,必須告訴Linux系統(tǒng)使用那(幾)個(gè)locale。這就需要對(duì)locale的內(nèi)部機(jī)制有一點(diǎn)點(diǎn)的了解。在前面我已經(jīng)提到過,locale把按照所涉及到的文化傳統(tǒng)的各個(gè)方面分成12個(gè)大類,這12個(gè)大類分別是:
    1、語言符號(hào)及其分類(LC_CTYPE)
    2、數(shù)字(LC_NUMERIC)
    3、比較和排序習(xí)慣(LC_COLLATE)
    4、時(shí)間顯示格式(LC_TIME)
    5、貨幣單位(LC_MONETARY)
    6、信息主要是提示信息,錯(cuò)誤信息, 狀態(tài)信息, 標(biāo)題, 標(biāo)簽, 按鈕和菜單等(LC_MESSAGES)
    7、姓名書寫方式(LC_NAME)
    8、地址書寫方式(LC_ADDRESS)
    9、電話號(hào)碼書寫方式(LC_TELEPHONE)
    10、度量衡表達(dá)方式(LC_MEASUREMENT)
    11、默認(rèn)紙張尺寸大小(LC_PAPER)
    12、對(duì)locale自身包含信息的概述(LC_IDENTIFICATION)。
    ?
    其中,與中文輸入關(guān)系最密切的就是 LC_CTYPE, LC_CTYPE 規(guī)定了系統(tǒng)內(nèi)有效的字符以及這些字符的分類,諸如什么是大寫字母,小寫字母,大小寫轉(zhuǎn)換,標(biāo)點(diǎn)符號(hào)、可打印字符和其他的字符屬性等方面。而locale定義zh_CN中最最重要的一項(xiàng)就是定義了漢字(Class “hanzi”)這一個(gè)大類,當(dāng)然也是用Unicode描述的,這就讓中文字符在Linux系統(tǒng)中成為合法的有效字符,而且不論它們是用什么字符集編碼的。
    ?
    LC_CTYPE
    % This is a copy of the "i18n" LC_CTYPE with the following modifications: - Additional classes: hanzi
    ?
    copy "i18n"
    ?
    class "hanzi"; /
    % <U3400>..<U4DBF>;/
    <U4E00>..<U9FA5>;/
    <UF92C>;<UF979>;<UF995>;<UF9E7>;<UF9F1>;<UFA0C>;<UFA0D>;<UFA0E>;/
    <UFA0F>;<UFA11>;<UFA13>;<UFA14>;<UFA18>;<UFA1F>;<UFA20>;<UFA21>;/
    <UFA23>;<UFA24>;<UFA27>;<UFA28>;<UFA29>
    END LC_CTYPE
    ?
    在en_US的locale定義中,并沒有定義漢字,所以漢字不是有效字符。所以如果要輸入中文必須使用支持中文的locale,也就是zh_XX,如zh_CN,zh_TW,zh_HK等等。
    ?
    另外非常重要的一點(diǎn)就是這些分類是彼此獨(dú)立的,也就是說LC_CTYPE,LC_COLLATE和 LC_MESSAGES等等分類彼此之間是獨(dú)立的,可以根據(jù)用戶的需要設(shè)定成不同的值。這一點(diǎn)對(duì)很多用戶是有利的,甚至是必須的。例如,我就需要一個(gè)能夠輸入中文的英文環(huán)境,所以我可以把LC_CTYPE設(shè)定成zh_CN.GB18030,而其他所有的項(xiàng)都是en_US.UTF-8。
    ?

    七、怎樣設(shè)定locale呢?
    ?
    設(shè)定locale就是設(shè)定12大類的locale分類屬性,即 12個(gè)LC_*。除了這12個(gè)變量可以設(shè)定以外,為了簡(jiǎn)便起見,還有兩個(gè)變量:LC_ALL和LANG。它們之間有一個(gè)優(yōu)先級(jí)的關(guān)系:
    LC_ALL>LC_*>LANG
    可以這么說,LC_ALL是最上級(jí)設(shè)定或者強(qiáng)制設(shè)定,而LANG是默認(rèn)設(shè)定值。
    1、如果你設(shè)定了LC_ALL=zh_CN.UTF-8,那么不管LC_*和LANG設(shè)定成什么值,它們都會(huì)被強(qiáng)制服從LC_ALL的設(shè)定,成為 zh_CN.UTF-8。
    2、假如你設(shè)定了LANG=zh_CN.UTF-8,而其他的LC_*=en_US.UTF-8,并且沒有設(shè)定LC_ALL的話,那么系統(tǒng)的locale設(shè)定以LC_*=en_US.UTF-8。
    3、假如你設(shè)定了LANG=zh_CN.UTF-8,而其他的LC_*,和LC_ALL均未設(shè)定的話,系統(tǒng)會(huì)將LC_*設(shè)定成默認(rèn)值,也就是LANG的值 zh_CN.UTF-8 。
    4、假如你設(shè)定了LANG=zh_CN.UTF-8,而其他的LC_CTYPE=en_US.UTF-8,其他的LC_*,和LC_ALL均未設(shè)定的話,那么系統(tǒng)的locale設(shè)定將是:LC_CTYPE=en_US.UTF-8,其余的 LC_COLLATE,LC_MESSAGES等等均會(huì)采用默認(rèn)值,也就是LANG的值,也就是LC_COLLATE=LC_MESSAGES=……= LC_PAPER=LANG=zh_CN.UTF-8。
    ?
    所以,locale是這樣設(shè)定的:
    1、如果你需要一個(gè)純中文的系統(tǒng)的話,設(shè)定LC_ALL= zh_CN.XXXX,或者LANG= zh_CN.XXXX都可以,當(dāng)然你可以兩個(gè)都設(shè)定,但正如上面所講,LC_ALL的值將覆蓋所有其他的locale設(shè)定,不要作無用功。
    2、如果你只想要一個(gè)可以輸入中文的環(huán)境,而保持菜單、標(biāo)題,系統(tǒng)信息等等為英文界面,那么只需要設(shè)定LC_CTYPE=zh_CN.XXXX,LANG=en_US.XXXX就可以了。這樣LC_CTYPE=zh_CN.XXXX,而LC_COLLATE=LC_MESSAGES=……= LC_PAPER=LANG=en_US.XXXX。
    3、假如你高興的話,可以把12個(gè)LC_*一一設(shè)定成你需要的值,打造一個(gè)古靈精怪的系統(tǒng):
    LC_CTYPE=zh_CN.GBK/GBK(使用中文編碼內(nèi)碼GBK字符集);
    LC_NUMERIC=en_GB.ISO-8859-1(使用大不列顛的數(shù)字系統(tǒng))
    LC_MEASUREMEN=de_DE@euro.ISO-8859-15(德國(guó)的度量衡使用ISO-8859-15字符集)
    羅馬的地址書寫方式,美國(guó)的紙張?jiān)O(shè)定……。估計(jì)沒人這么干吧。
    4、假如你什么也不做的話,也就是LC_ALL,LANG和LC_*均不指定特定值的話,系統(tǒng)將采用POSIX作為lcoale,也就是C locale。

    posted @ 2006-08-16 10:45 GHawk 閱讀(757) | 評(píng)論 (0)編輯 收藏

    PostgreSQL的主機(jī)認(rèn)證配置

    轉(zhuǎn)自 http://www.linuxsir.org/bbs/showthread.php?t=32116

    pg_hba.conf 文件
    客戶端認(rèn)證是由 $PGDATA 目錄里的文件pg_hba.conf 控制的,也就是說, /usr/local/pgsql/data/pg_hba.conf. (HBA 的意思是 host-based authentication:基于主機(jī)的認(rèn)證.) 在initdb初始化數(shù)據(jù)區(qū)的時(shí)候,它會(huì) 安裝一個(gè)缺省的文件.

    文件 pg_hba.conf 的常用格式是一套記錄, 每行一條。空白行或者井號(hào)(“#”)開頭的行被忽略。一條記錄 是由若干用空格和/或 tab 分隔的字段組成。

    每條記錄可以下面三種格式之一

    local database authentication-method [ authentication-option ]
    host database IP-address IP-mask authentication-method [ authentication-option ]
    hostssl database IP-address IP-mask authentication-method [ authentication-option ]

    各個(gè)字段的含義如下:

    local
    這條記錄適用于通過 Unix 域套接字的聯(lián)接.

    host
    這條記錄適用于通過 TCP/IP 網(wǎng)絡(luò)的聯(lián)接.請(qǐng)注意,除非服務(wù)器是 帶著 -i 選項(xiàng)或者等效的配置參數(shù)集啟動(dòng)的,否則 TCP/IP 聯(lián)接將完全被禁止掉.

    hostssl
    這條記錄適用于試圖建立在 TCP/IP 上的 SSL 之上的聯(lián)接. 要使用這個(gè)選項(xiàng),服務(wù)器必須帶著 SSL 支持編譯.而且在服務(wù)器啟動(dòng)的時(shí)候, 必須用 -l 選項(xiàng) 或等效的配置設(shè)置打開 SSL.

    database
    聲明記錄所適用的數(shù)據(jù)庫(kù)。值 all 表明該記錄應(yīng)用于所有數(shù)據(jù)庫(kù), 值 sameuser 表示于正在聯(lián)接的用戶同名的數(shù)據(jù)庫(kù)。 否則它就是某個(gè)具體的 Postgres 數(shù)據(jù)庫(kù)名字.

    IP address, IP mask
    這兩個(gè)字段以各主機(jī)的 IP 地址為基礎(chǔ), 控制一條 host 記錄應(yīng)用于哪個(gè)主機(jī). (當(dāng)然,IP 地址可能會(huì)被欺騙(spoofed),但是這個(gè)考慮 超過了 Postgres 的考慮范圍.) 準(zhǔn)確的邏輯是,對(duì)于匹配的記錄

    (actual-IP-address xor IP-address-field) and IP-mask-field
    必需為零.

    authentication method(認(rèn)證方法)
    聲明一個(gè)用戶在與該數(shù)據(jù)庫(kù)聯(lián)接的時(shí)候必須使用的認(rèn)證方法. 可能的選擇如下,詳細(xì)情況在 Section 4.2.


    trust
    無條件地允許聯(lián)接.這個(gè)方法允許任何有登錄客戶機(jī)權(quán)限的用戶以任意 Postgres 數(shù)據(jù)庫(kù)用戶身份進(jìn)行聯(lián)接.

    reject
    聯(lián)接無條件拒絕.常用于從組中“過濾”某些主機(jī).

    password
    要求客戶端在嘗試聯(lián)接的時(shí)候提供一個(gè)口令, 這個(gè)口令與為該用戶設(shè)置的口令必須匹配.

    在 password 關(guān)鍵字后面可以聲明一個(gè)可選的文件名. 這個(gè)文件包含一個(gè)用戶列表,列表記錄的是那些適用口令認(rèn)證記錄的用戶, 以及可選的候選口令.

    口令是以明文的方式在線路上傳輸?shù)模绻玫谋Wo(hù),請(qǐng)使用 crypt 方法.

    crypt
    類似 password 方法,但是口令是用一種簡(jiǎn)單的 口令對(duì)應(yīng)協(xié)議加密后在線路上傳送的.這么做在密碼學(xué)理論上是不安全的, 但可以防止偶然的線路偵聽.在 crypt 關(guān)鍵字后面 可以有一個(gè)文件,文件里包含適用口令認(rèn)證記錄的用戶列表.

    krb4
    用 Kerberos V4 認(rèn)證用戶.只有在進(jìn)行 TCP/IP 聯(lián)接的時(shí)候才能用. (譯注:Kerberos,"克爾波洛斯",故希臘神話冥王哈得斯的多頭看門狗. Kerberos 是 MIT 開發(fā)出來的基與對(duì)稱加密算法的認(rèn)證協(xié)議和/或密鑰 交換方法.其特點(diǎn)是需要兩個(gè)不同用途的服務(wù)器,一個(gè)用于認(rèn)證身份, 一個(gè)用于通道兩端用戶的密鑰交換.同時(shí) Kerberos 對(duì)網(wǎng)絡(luò)時(shí)間同步 要求比較高,以防止回放攻擊,因此通常伴隨 NTP 服務(wù).)

    krb5
    用 Kerberos V5 認(rèn)證用戶.只有在進(jìn)行 TCP/IP 聯(lián)接的時(shí)候才能用. (譯注:Kerberos V5 是上面 V4 的改良,主要是不再依賴 DES 算法, 同時(shí)增加了一些新特性.)

    ident
    服務(wù)器將詢問客戶機(jī)上的 ident 服務(wù)器以核實(shí)正在聯(lián)接的用戶身份. 然后 Postgres 核實(shí)該操作系統(tǒng)用戶是否被允許以其請(qǐng)求的數(shù)據(jù)庫(kù)用戶身份與數(shù)據(jù)庫(kù)聯(lián)接. 只有在使用 TCP/IP 聯(lián)接的時(shí)候才能用這個(gè)選項(xiàng). 跟在 ident 關(guān)鍵字后面的 authentication option 聲明一個(gè) ident map(身份映射), 該文件聲明那些操作系統(tǒng)用戶等效于數(shù)據(jù)庫(kù)用戶.見下文獲取詳細(xì)信息.


    authentication option(認(rèn)證選項(xiàng))
    這個(gè)字段根據(jù)不同的認(rèn)證方法(authentication method)有不同的 解釋.

    認(rèn)證時(shí)使用與聯(lián)接請(qǐng)求的客戶端 IP 地址和所要求的 數(shù)據(jù)庫(kù)名字匹配的第一條記錄. 請(qǐng)注意這里沒有 “fall-through(越過)” 或者 “backup(備份)”:如果選定了一條記錄但認(rèn)證失敗, 那么將不會(huì)繼續(xù)考慮下面的記錄.如果沒有匹配的記錄,則拒絕訪問.

    在每次聯(lián)接的請(qǐng)求時(shí),文件 pg_hba.conf 都會(huì)被重新讀取.因此很容易就能在服務(wù)器運(yùn)行的時(shí)候修改訪問權(quán)限.

    在 Example 4-1 里是 pg_hba.conf 的一個(gè)例子. 閱讀下文理解不同認(rèn)證方法的細(xì)節(jié).

    Example 4-1. 一個(gè) pg_hba.conf 文件的例子

    # TYPE DATABASE IP_ADDRESS MASK AUTHTYPE MAP

    # 允許在本機(jī)上的任何用戶以任何身份聯(lián)接任何數(shù)據(jù)庫(kù)
    # 但必須是通過 IP 進(jìn)行聯(lián)接

    host all 127.0.0.1 255.255.255.255 trust

    # 同樣,但用的是 Unix-套接字聯(lián)接

    local all trust

    # 允許 IP 地址為 192.168.93.x 的任何主機(jī)與數(shù)據(jù)庫(kù)
    # "template1" 相連,用與他們?cè)谧约旱闹鳈C(jī)上相同 ident 的用戶名標(biāo)識(shí)他自己
    # (通常是他的 Unix 用戶名)

    host template1 192.168.93.0 255.255.255.0 ident sameuser

    # 允許來自主機(jī) 192.168.12.10 的用戶與 "template1" 數(shù)據(jù)庫(kù)聯(lián)接,
    # 只要該用戶提供了在 pg_shadow 里正確的口令.

    host template1 192.168.12.10 255.255.255.255 crypt

    # 如果前面沒有其它 "host" 行,那么下面兩行將拒絕所有來自
    # 192.168.54.1 的聯(lián)接請(qǐng)求 (因?yàn)榍懊娴挠涗浵绕ヅ?br /># 但是允許來自互聯(lián)網(wǎng)上其它任何地方有效的 Kerberos V5 認(rèn)證的聯(lián)接
    # 零掩碼表示不考慮主機(jī) IP 的任何位.因此它匹配任何主機(jī):

    host all 192.168.54.1 255.255.255.255 reject
    host all 0.0.0.0 0.0.0.0 krb5

    # 允許來自 192.168.x.x 的任何用戶與任意數(shù)據(jù)庫(kù)聯(lián)接,只要他們通過 ident 檢查
    # 但如果 ident 說該用戶是 "bryanh" 而他要求以 PostgreSQL 用戶 "guest1" 聯(lián)接,
    # 那么只有在 `pg_ident.conf' 里有 "omicron" 的映射,說 "bryanh" 允許以
    # "guest1" 進(jìn)行聯(lián)接時(shí)才真正可以進(jìn)行聯(lián)接.

    host all 192.168.0.0 255.255.0.0 ident omicron

    posted @ 2006-06-07 10:42 GHawk 閱讀(1214) | 評(píng)論 (0)編輯 收藏

    UP & XP之爭(zhēng),意義何在?(續(xù))

    雖然我沒能去參加BEA的活動(dòng),但是相關(guān)的資料已經(jīng)下載并且瀏覽過了,確實(shí)收獲不少。所以,對(duì)于莊兄的這些想法我很理解。

    相信不只你我,大部分的人都比較認(rèn)同敏捷化的過程,希望使過程變得敏捷。的確,這是個(gè)好東西,之前我也說過“敏捷過程是三贏的”這樣的話。

    我所關(guān)心的問題是“如何能夠用好XP?”。

    莊兄認(rèn)為“湯的味道,不需要什么過程控制”,我也會(huì)認(rèn)同。為什么?因?yàn)槟阄叶际侵袊?guó)人。大部分中國(guó)人不會(huì)認(rèn)為湯的味道需要什么過程控制。但是想想看,如果你在不同地方買到的肯德基炸雞味道各異;同一批次生產(chǎn)的同型號(hào)的汽車形狀各異;銀行里取出來的一疊百元大鈔大小不一,你不會(huì)覺得奇怪么或是有那么一點(diǎn)點(diǎn)憤怒么?

    西方人(甚至學(xué)習(xí)西方的日本人)對(duì)品質(zhì)的重視程度卻完全不同。他們不允許肯德基炸雞的味道有很大偏差(即便你覺得無所謂);“2毫米工程”不允許整車的總裝長(zhǎng)度發(fā)生2毫米以上的偏差(即便你覺得無所謂);百元大鈔……(我想誰都不會(huì)無所謂)。

    所以,一切質(zhì)量都有標(biāo)準(zhǔn),一切標(biāo)準(zhǔn)都應(yīng)該被度量!這就是工程學(xué)的目標(biāo)之一,為了實(shí)現(xiàn)更嚴(yán)格的質(zhì)量標(biāo)準(zhǔn),就需要過程控制和度量。

    莊兄所說,用測(cè)試用例保證代碼的質(zhì)量其實(shí)還是采用了“測(cè)試用例”作為度量的標(biāo)準(zhǔn)。唯一的問題是:“如何確保測(cè)試用例的質(zhì)量”。顯然,我們不能把一把不直的尺子度量出來的結(jié)果作為可靠的參考依據(jù)。怎么解決呢?“結(jié)對(duì)編程”么?嗯,這是一個(gè)不錯(cuò)的方式,那么最終該信賴誰呢?是Pair中的A還是B呢?或者,是Leader么?那么又是誰提出的要求呢?是老板么?還是客戶?政府?法規(guī)?市場(chǎng)?……問題沒有終結(jié)了。

    不要學(xué)習(xí)哲學(xué)家的方法,提出一層又一層無法解決的問題。我們是工程師,應(yīng)該試圖解決問題才對(duì)!解決問題的關(guān)鍵在于,XP同樣需要標(biāo)準(zhǔn)!為了制定標(biāo)準(zhǔn),必要的文檔是不可以少的。而且,標(biāo)準(zhǔn)本身的質(zhì)量是嚴(yán)苛的。因?yàn)椋鳛闃?biāo)準(zhǔn),他不可以含糊其辭、模棱兩可。在標(biāo)準(zhǔn)的基礎(chǔ)之上,我們才可以談什么TDD、Pair Programming之類的實(shí)踐。

    回到爭(zhēng)論的開端。我引用了林先生的話“UP是正楷;XP是草書。要先學(xué)好UP才能學(xué)好XP,先學(xué)XP會(huì)亂套。”我對(duì)這句話的理解如下:這句話并沒有批判UP或是XP,只是指出了一個(gè)學(xué)習(xí)的順序。我認(rèn)為這句話是有實(shí)踐依據(jù)的,因?yàn)閁P強(qiáng)調(diào)的是一種經(jīng)典的工程方法。軟件工程本來就源于其他行業(yè)的工程實(shí)踐經(jīng)驗(yàn)。UP利用大量的文檔對(duì)開發(fā)活動(dòng)進(jìn)行約束和記錄。正是這種重量級(jí)的過程規(guī)范了規(guī)范了從PM到Coder的所有活動(dòng),有問題可以參照文檔,看看自己應(yīng)該怎么做。文檔也可以作為日后評(píng)估這個(gè)過程的依據(jù)。隨著整個(gè)團(tuán)隊(duì)和每個(gè)個(gè)人的經(jīng)驗(yàn)不斷積累,開發(fā)活動(dòng)中的日常行為漸漸形成了一種職業(yè)習(xí)慣。然后可以通過對(duì)UP的配置,逐漸減少文檔的使用量,一些沒有必要的文檔就可以省去,更具團(tuán)隊(duì)的實(shí)際能力調(diào)整過程。UP是可配置的,不必要的文檔沒有存在的理由,這一點(diǎn)UP和XP沒有什么兩樣。當(dāng)然,隨著大家的職業(yè)習(xí)慣越來越好,經(jīng)驗(yàn)越來越豐富,個(gè)人和團(tuán)隊(duì)就可以采用更敏捷更輕便的過程,逐漸過渡到XP上去。

    反過來,如果一開始就沒有詳盡的文檔,很多活動(dòng)(比如設(shè)計(jì)、版本控制)往往會(huì)脫離控制,進(jìn)入一種無序的、混亂的狀態(tài)。沒有文檔可參考,就意味著很多問題只能問人,而不同人的回答可能各異,同一個(gè)人對(duì)同一個(gè)問題的兩次回答也可能不同!當(dāng)然,如果整個(gè)團(tuán)隊(duì)的工程素養(yǎng)和個(gè)體的職業(yè)習(xí)慣都比較好的情況下可能不會(huì)發(fā)生類似的情況。但是這種工程素養(yǎng)和職業(yè)習(xí)慣從哪里來,可能單靠的XP是不足以培養(yǎng)出來的。

    “UP是正楷;XP是草書。要先學(xué)好UP才能學(xué)好XP,先學(xué)XP會(huì)亂套。”這句話表明了UP和XP在一定程度上是存在沖突的,并且提出了一條路線去降低和避免這個(gè)沖突。

    再次需要強(qiáng)調(diào)的是莊兄所提到的“XP是一種思想”,這點(diǎn)我認(rèn)同。但是我認(rèn)為這個(gè)除了思想之外,還是一種“文化”。這種思想和文化也是出于軟件工程多年來的實(shí)踐,其中也不免有UP等其他過程。不能簡(jiǎn)單地認(rèn)為“我們只要吸取歷史的教訓(xùn),提出新的思想和文化就不會(huì)再犯同樣的錯(cuò)誤了。”很多時(shí)候歷史總是一次又一次地重演著。新的思想和文化如果不能被準(zhǔn)確地理解和運(yùn)用,它所帶來的可能仍然是它原本想解決的問題。只有我們具備了引入這種文化的基礎(chǔ),才能把它變成自己的文化,否則這仍然是掛在嘴邊行于表面的一種不求精髓只求模仿的偽文化、偽思想。這一點(diǎn)對(duì)于UP和XP的實(shí)踐者來說沒有什么兩樣。

    posted @ 2006-04-25 15:03 GHawk 閱讀(2050) | 評(píng)論 (4)編輯 收藏

    UP & XP之爭(zhēng),意義何在?

    不光是做軟件,凡是做產(chǎn)品,最后關(guān)注的總是產(chǎn)品的質(zhì)量

    舉個(gè)例子,比如你做一鍋湯:
    今天你狀態(tài)很好,做完后嘗了嘗,感覺很美味,你的家人嘗了以后也有同感,喝完后感覺心情舒暢、意猶未盡。
    隔了一個(gè)禮拜,你做同樣的湯給家里人喝。做完后你嘗了嘗,感覺依然美味,盼望著得到家人的賞識(shí),然而他們卻說味道咸了點(diǎn)。你很奇怪,為什么同樣自己嘗過了,家里人卻感覺不一樣呢?是不是最近加班多了,休息不好,味覺不準(zhǔn)了?
    一個(gè)月過后,你要去國(guó)外出差,給家里請(qǐng)了個(gè)臨時(shí)保姆。一天,他也做了這么個(gè)湯,做完后,他也嘗了嘗,感覺口味很不錯(cuò),可是端上桌,家里人說這湯太辣了。原來這保姆才從湖南老家出來不久……

    因此,只把焦點(diǎn)放在最后的產(chǎn)品上往往是不夠的。需要對(duì)“做湯的過程”加以控制。所以工程界會(huì)比較關(guān)注過程的管理,在軟件領(lǐng)域也稱作“軟件生命周期管理”。

    再來看看UP和XP。它們都屬于軟件過程,只不過各有特色。

    再拿剛才那個(gè)做湯的例子:
    大家都聽說過德國(guó)人的廚房像化學(xué)實(shí)驗(yàn)室,天平、計(jì)時(shí)器、量杯……裝備齊全,再配上精確的菜譜,嚴(yán)謹(jǐn)?shù)牡聡?guó)人能夠確保不用嘗那最后一口都做出口味基本一致的湯。
    換了中國(guó)人,大部分人都不會(huì)模仿德國(guó)人做菜的方式。解決方案很簡(jiǎn)單,讓你的太太和孩子都嘗那最后一口,再根據(jù)反饋調(diào)整幾次,同樣能做出全家人滿意的湯。

    這個(gè)例子也許不太貼切,但是可以聯(lián)想一下:德國(guó)人做湯傾向于UP;中國(guó)人做湯傾向于XP

    UP和XP最終目的都是為了保證產(chǎn)品的質(zhì)量,不同的是,兩個(gè)過程所強(qiáng)調(diào)的方法不同。我想,沒有人會(huì)說“UP的目的在于變態(tài)地追求文檔的完美”、“UP是為了要程序員學(xué)會(huì)寫各種各樣文檔”……之類的話。同時(shí),也沒人會(huì)說“XP就是不要文檔只要代碼”、“XP就是要變態(tài)地追求完美的代碼”……這樣的話。

    這些不正確的看法,只是人們對(duì)于這兩種過程的誤解。或許是來自于開發(fā)人員和項(xiàng)目經(jīng)理的那些“不堪回首的經(jīng)歷”。

    “UP害慘了整個(gè)軟件行業(yè),讓開發(fā)人員沒完沒了地寫文檔而忽略了代碼,XP才是王道”這樣的話,我不敢茍同,仍然有很多企業(yè)使用著UP這樣的重型軟件工程,就好比德國(guó)人依然喜歡把廚房弄得像個(gè)實(shí)驗(yàn)室。

    XP固然是個(gè)好東西。但是,不知道大多數(shù)人對(duì)于XP的熱衷是出于對(duì)XP文化的理解,還是國(guó)人慣有的“一窩蜂”似的行為。不曉得一個(gè)“能夠熟練閱讀代碼的Leader”是不是能夠真正運(yùn)用好XP,確保他的團(tuán)隊(duì)能夠盡可能少地出現(xiàn)"Over engineering"這種違背Agile精神的東西,或是能夠讓他的團(tuán)隊(duì)保證“每周只工作40小時(shí)”這樣的基本實(shí)踐?

    對(duì)于不同的技術(shù)和過程,應(yīng)該給予冷靜的分析和慎重的選擇。每個(gè)過程和技術(shù)都不能以“正確”或“不正確”來定性,只能以“合適”和“不合適”來定性。因?yàn)檎_或不正確是要嚴(yán)格證明的,而合適不合適是來源于工程實(shí)踐的結(jié)果。所以,COBOL依然在金融領(lǐng)域起著舉足輕重的作用,科學(xué)家們?nèi)圆煌麱ortran,匯編和C仍然健在……

    另外不得不提的是文化上的差異。為什么很多時(shí)候,我們學(xué)習(xí)國(guó)外的先進(jìn)技術(shù),購(gòu)買了整套生產(chǎn)線,引進(jìn)了全套圖紙,請(qǐng)國(guó)外專家做了詳細(xì)的全程化培訓(xùn),國(guó)人生產(chǎn)出的產(chǎn)品品質(zhì)依然不如國(guó)外原產(chǎn)的?這是每個(gè)中國(guó)人都應(yīng)該思考的問題……

    ?

    posted @ 2006-04-23 18:28 GHawk 閱讀(1900) | 評(píng)論 (4)編輯 收藏

    對(duì)"UP是正楷,XP是草書"的反思

    “UP是正楷,XP是草書。先學(xué)好了UP,才能學(xué)好XP;先學(xué)XP再學(xué)UP就會(huì)亂套。?”

    老師曾這么說。最近,對(duì)這句話有了深刻的體會(huì)。

    軟件過程是一個(gè)以人為中心的活動(dòng)。人是項(xiàng)目中最難確定和控制的因素。休息的質(zhì)量、情緒的起伏都會(huì)影響整個(gè)活動(dòng)。為了盡可能地約束這種個(gè)體的不確定行為和減少開發(fā)過程中不必要的誤會(huì)。"UP"采用了大量的文檔來對(duì)整個(gè)開發(fā)過程進(jìn)行控制。這些文檔主要分為以下幾類:

    • 計(jì)劃文檔——項(xiàng)目的開發(fā)計(jì)劃、迭代計(jì)劃、測(cè)試計(jì)劃等。
    • 技術(shù)文檔——項(xiàng)目的設(shè)計(jì)文檔、某個(gè)操作的說明文檔等。
    • 記錄文檔——日常的會(huì)議紀(jì)要、每日進(jìn)度反饋、評(píng)估報(bào)告等。

    文檔成了UP活動(dòng)的主要部分。在UP中,往往大量的資源用于文檔的制作。這些文檔的目的是為了盡可能減少不必要的溝通成本和誤會(huì),也為了在發(fā)生問題的時(shí)候能夠盡快確定原因找到解決方法。

    而正是因?yàn)槿绱朔敝氐馁Y源消耗,導(dǎo)致真正的設(shè)計(jì)和代碼只占到了總開銷的很少部分。這對(duì)很多人來說不可理解,甚至覺得本末倒置。于是很多敏捷方法誕生了,最具代表性也是對(duì)UP思想最具顛覆性的就屬XP了。

    對(duì)外,XP以快速的反應(yīng)速度來響應(yīng)客戶的需求;對(duì)內(nèi),XP以高質(zhì)量的代碼和設(shè)計(jì)來確保盡可能不產(chǎn)生不必要的文檔和資源開銷。

    從表面上看,在當(dāng)今,XP確實(shí)是一種非常理想的開發(fā)過程。

    但是,從沒有過程到XP往往會(huì)非常失敗。這是為什么?問題的關(guān)鍵還在于人。

    • 無過程-->UP -->XP

    UP利用文檔來約束和規(guī)范人們的開發(fā)活動(dòng)。當(dāng)一個(gè)沒有經(jīng)驗(yàn)的團(tuán)隊(duì)經(jīng)歷UP后,就等于把性格各異、習(xí)慣差別不同的人統(tǒng)一成了“相對(duì)較一致的開發(fā)人員”。

    他們有一致的編碼習(xí)慣,有共同的用語,有嚴(yán)格的規(guī)則。隨著經(jīng)驗(yàn)的積累,這個(gè)團(tuán)隊(duì)間的默契越來越高。此時(shí),如果過程由UP向XP切換,付出的代價(jià)就會(huì)相對(duì)較低。

    • 無過程-->XP-->UP

    XP主張快速反應(yīng)。如果一個(gè)沒有經(jīng)驗(yàn)的團(tuán)隊(duì)在一開始就嘗試XP,那么后果可能是慘痛的。因?yàn)橐粋€(gè)沒有經(jīng)驗(yàn)的團(tuán)隊(duì)其成員間的相互了解頗少,對(duì)于一件事,往往十個(gè)人有十種想法。當(dāng)缺少文檔約束時(shí),在以代碼和設(shè)計(jì)為中心的活動(dòng)中,成員之間往往因?yàn)樗降膮⒉畈积R導(dǎo)致無休止的討論甚至爭(zhēng)論,代碼被不必要地頻繁改動(dòng)。這是因?yàn)椋?font color="#ff0000">在團(tuán)隊(duì)建設(shè)早期,成員之間往往連最基本的尊重和信任都不存在。 這種無意義的活動(dòng)往往會(huì)嚴(yán)重影響項(xiàng)目的正常進(jìn)行。

    所以,學(xué)習(xí)和應(yīng)用過程不僅僅是個(gè)體的事,而是整個(gè)團(tuán)隊(duì)的事。只有當(dāng)團(tuán)隊(duì)采用嚴(yán)格文檔化的過程并且經(jīng)過磨合后,才能漸漸向輕量級(jí)的過程遷移,逐漸將不必要的文檔刪減掉,采用更靈活的過程。但是,此時(shí)并不是“沒有文檔”而是“心中有文檔”。

    posted @ 2006-03-01 16:25 GHawk 閱讀(1656) | 評(píng)論 (4)編輯 收藏

    加載Classpath中的文件(轉(zhuǎn))

       URL url = this.getClass().getResource("EJBConfig.xml");
            
    try {
                File xmlFile 
    = new File(URLDecoder.decode(url.getFile(),"UTF-8"));
                
    if(xmlFile.exists())
                    System.out.println(
    "OK");
            } 
    catch (UnsupportedEncodingException e) {
                e.printStackTrace();  
    //To change body of catch statement use File | Settings | File Templates.
            }

    posted @ 2006-01-19 22:07 GHawk 閱讀(765) | 評(píng)論 (0)編輯 收藏

    敏捷軟件開發(fā) 讀書筆記 (4)——OO五大原則(3.LSP——里氏替換原則)

    OCP作為OO的高層原則,主張使用“抽象(Abstraction)”和“多態(tài)(Polymorphism)”將設(shè)計(jì)中的靜態(tài)結(jié)構(gòu)改為動(dòng)態(tài)結(jié)構(gòu),維持設(shè)計(jì)的封閉性。

    “抽象”是語言提供的功能。“多態(tài)”由繼承語義實(shí)現(xiàn)。

    如此,問題產(chǎn)生了:“我們?nèi)绾稳ザ攘坷^承關(guān)系的質(zhì)量?”

    Liskov于1987年提出了一個(gè)關(guān)于繼承的原則“Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.”——“繼承必須確保超類所擁有的性質(zhì)在子類中仍然成立。”也就是說,當(dāng)一個(gè)子類的實(shí)例應(yīng)該能夠替換任何其超類的實(shí)例時(shí),它們之間才具有is-A關(guān)系。

    該原則稱為L(zhǎng)iskov Substitution Principle——里氏替換原則。林先生在上課時(shí)風(fēng)趣地稱之為“老鼠的兒子會(huì)打洞”。^_^

    我們來研究一下LSP的實(shí)質(zhì)。學(xué)習(xí)OO的時(shí)候,我們知道,一個(gè)對(duì)象是一組狀態(tài)和一系列行為的組合體。狀態(tài)是對(duì)象的內(nèi)在特性,行為是對(duì)象的外在特性。LSP所表述的就是在同一個(gè)繼承體系中的對(duì)象應(yīng)該有共同的行為特征。

    這一點(diǎn)上,表明了OO的繼承與日常生活中的繼承的本質(zhì)區(qū)別。舉一個(gè)例子:生物學(xué)的分類體系中把企鵝歸屬為鳥類。我們模仿這個(gè)體系,設(shè)計(jì)出這樣的類和關(guān)系。

     lsp-fig1.jpg

    類“鳥”中有個(gè)方法fly,企鵝自然也繼承了這個(gè)方法,可是企鵝不能飛阿,于是,我們?cè)谄簌Z的類中覆蓋了fly方法,告訴方法的調(diào)用者:企鵝是不會(huì)飛的。這完全符合常理。但是,這違反了LSP,企鵝是鳥的子類,可是企鵝卻不能飛!需要注意的是,此處的“鳥”已經(jīng)不再是生物學(xué)中的鳥了,它是軟件中的一個(gè)類、一個(gè)抽象。

    有人會(huì)說,企鵝不能飛很正常啊,而且這樣編寫代碼也能正常編譯,只要在使用這個(gè)類的客戶代碼中加一句判斷就行了。但是,這就是問題所在!首先,客戶代碼和“企鵝”的代碼很有可能不是同時(shí)設(shè)計(jì)的,在當(dāng)今軟件外包一層又一層的開發(fā)模式下,你甚至根本不知道兩個(gè)模塊的原產(chǎn)地是哪里,也就談不上去修改客戶代碼了。客戶程序很可能是遺留系統(tǒng)的一部分,很可能已經(jīng)不再維護(hù),如果因?yàn)樵O(shè)計(jì)出這么一個(gè)“企鵝”而導(dǎo)致必須修改客戶代碼,誰應(yīng)該承擔(dān)這部分責(zé)任呢?(大概是上帝吧,誰叫他讓“企鵝”不能飛的。^_^)“修改客戶代碼”直接違反了OCP,這就是OCP的重要性。違反LSP將使既有的設(shè)計(jì)不能封閉!

    修正后的設(shè)計(jì)如下:

     lsp-fig2.jpg

    但是,這就是LSP的全部了么?書中給了一個(gè)經(jīng)典的例子,這又是一個(gè)不符合常理的例子:正方形不是一個(gè)長(zhǎng)方形。這個(gè)悖論的詳細(xì)內(nèi)容能在網(wǎng)上找到,我就不多廢話了。

    LSP并沒有提供解決這個(gè)問題的方案,而只是提出了這么一個(gè)問題。

    于是,工程師們開始關(guān)注如何確保對(duì)象的行為。1988年,B. Meyer提出了Design by Contract(契約式設(shè)計(jì))理論。DbC從形式化方法中借鑒了一套確保對(duì)象行為和自身狀態(tài)的方法,其基本概念很簡(jiǎn)單:

    1. 每個(gè)方法調(diào)用之前,該方法應(yīng)該校驗(yàn)傳入?yún)?shù)的正確性,只有正確才能執(zhí)行該方法,否則認(rèn)為調(diào)用方違反契約,不予執(zhí)行。這稱為前置條件(Pre-condition)。
    2. 一旦通過前置條件的校驗(yàn),方法必須執(zhí)行,并且必須確保執(zhí)行結(jié)果符合契約,這稱之為后置條件(Post-condition)。
    3. 對(duì)象本身有一套對(duì)自身狀態(tài)進(jìn)行校驗(yàn)的檢查條件,以確保該對(duì)象的本質(zhì)不發(fā)生改變,這稱之為不變式(Invariant)。

    以上是單個(gè)對(duì)象的約束條件。為了滿足LSP,當(dāng)存在繼承關(guān)系時(shí),子類中方法的前置條件必須與超類中被覆蓋的方法的前置條件相同或者更寬松;而子類中方法的后置條件必須與超類中被覆蓋的方法的后置條件相同或者更為嚴(yán)格。

    一些OO語言中的特性能夠說明這一問題:

    • 繼承并且覆蓋超類方法的時(shí)候,子類中的方法的可見性必須等于或者大于超類中的方法的可見性,子類中的方法所拋出的受檢異常只能是超類中對(duì)應(yīng)方法所拋出的受檢異常的子類。
      public class SuperClass{
          
      public void methodA() throws IOException{}
      }


      public class SubClassA extends SuperClass{
          
      //this overriding is illegal.
          private void methodA() throws Exception{}
      }


      public class SubClassB extends SuperClass{
          
      //this overriding is OK.
          public void methodA() throws FileNotFoundException{}
      }

    • 從Java5開始,子類中的方法的返回值也可以是對(duì)應(yīng)的超類方法的返回值的子類。這叫做“協(xié)變”(Covariant)
      public class SuperClass {
          
      public Number caculate(){
              
      return null;
          }

      }


      public class SubClass extends SuperClass{
          
      //only compiles in Java 5 or later.
          public Integer caculate(){
              
      return null;
          }

      }

    可以看出,以上這些特性都非常好地遵循了LSP。但是DbC呢?很遺憾,主流的面向?qū)ο笳Z言(不論是動(dòng)態(tài)語言還是靜態(tài)語言)還沒有加入對(duì)DbC的支持。但是隨著AOP概念的產(chǎn)生,相信不久DbC也將成為OO語言的一個(gè)重要特性之一。

    一些題外話:

    前一陣子《敲響OO時(shí)代的喪鐘》和《喪鐘為誰而鳴》兩篇文章引來了無數(shù)議論。其中提到了不少OO語言的不足。事實(shí)上,遵從LSP和OCP,不管是靜態(tài)類型還是動(dòng)態(tài)類型系統(tǒng),只要是OO的設(shè)計(jì),就應(yīng)該對(duì)對(duì)象的行為有嚴(yán)格的約束。這個(gè)約束并不僅僅體現(xiàn)在方法簽名上,而是這個(gè)具體行為的本身。這才是LSP和DbC的真諦。從這一點(diǎn)來說并不能說明“萬事萬物皆對(duì)象”的動(dòng)態(tài)語言和“C++,Java”這種“按接口編程”語言的優(yōu)劣,兩類語言都有待于改進(jìn)。莊兄對(duì)DJ的設(shè)想倒是開始引入DbC的概念了。這一點(diǎn)還是非常值得期待的。^_^
    另外,接口的語義正被OCP、LSP、DbC這樣的概念不斷地強(qiáng)化,接口表達(dá)了對(duì)象行為之間的“契約”關(guān)系。而不是簡(jiǎn)單地作為一種實(shí)現(xiàn)多繼承的語法糖。

    posted @ 2006-01-18 18:12 GHawk 閱讀(3973) | 評(píng)論 (2)編輯 收藏

    敏捷軟件開發(fā) 讀書筆記 (3)——OO五大原則(2.OCP——開閉原則)

    開閉原則很簡(jiǎn)單,一句話:“Closed for Modification; Open for Extension”——“對(duì)變更關(guān)閉;對(duì)擴(kuò)展開放”。開閉原則其實(shí)沒什么好講的,我將其歸結(jié)為一個(gè)高層次的設(shè)計(jì)總則。就這一點(diǎn)來講,OCP的地位應(yīng)該比SRP優(yōu)先。

    OCP的動(dòng)機(jī)很簡(jiǎn)單:軟件是變化的。不論是優(yōu)質(zhì)的設(shè)計(jì)還是低劣的設(shè)計(jì)都無法回避這一問題。OCP說明了軟件設(shè)計(jì)應(yīng)該盡可能地使架構(gòu)穩(wěn)定而又容易滿足不同的需求。

    為什么要OCP?答案也很簡(jiǎn)單——重用。

    “重用”,并不是什么軟件工程的專業(yè)詞匯,它是工程界所共用的詞匯。早在軟件出現(xiàn)前,工程師們就在實(shí)踐“重用”了。比如機(jī)械產(chǎn)品,通過零部件的組裝得到最終的能夠使用的工具。由于機(jī)械部件的設(shè)計(jì)和制造過程是極其復(fù)雜的,所以互換性是一個(gè)重要的特性。一輛車可以用不同的發(fā)動(dòng)機(jī)、不同的變速箱、不同的輪胎……很多東西我們直接買來裝上就可以了。這也是一個(gè)OCP的例子。(可能是由于我是搞機(jī)械出身的吧,所以就舉些機(jī)械方面的例子^_^)。

    如何在OO中引入OCP原則?把對(duì)實(shí)體的依賴改為對(duì)抽象的依賴就行了。下面的例子說明了這個(gè)過程:

    05賽季的時(shí)候,一輛F1賽車有一臺(tái)V10引擎。但是到了06賽季,國(guó)際汽聯(lián)修改了規(guī)則,一輛F1賽車只能安裝一臺(tái)V8引擎。車隊(duì)很快投入了新賽車的研發(fā),不幸的是,從工程師那里得到消息,舊車身的設(shè)計(jì)不能夠裝進(jìn)新研發(fā)的引擎。我們不得不為新的引擎重新打造車身,于是一輛新的賽車誕生了。但是,麻煩的事接踵而來,國(guó)際汽聯(lián)頻頻修改規(guī)則,搞得設(shè)計(jì)師在“賽車”上改了又改,最終變得不成樣子,只能把它廢棄。

    OCP-fig1.JPG

    為了能夠重用這輛昂貴的賽車,工程師們提出了解決方案:首先,在車身的設(shè)計(jì)上預(yù)留出安裝引擎的位置和管線。然后,根據(jù)這些設(shè)計(jì)好的規(guī)范設(shè)計(jì)引擎(或是引擎的適配器)。于是,新的賽車設(shè)計(jì)方案就這樣誕生了。

     OCP-fig2.JPG

    顯然,通過重構(gòu),這里應(yīng)用的是一個(gè)典型的Bridge模式。這個(gè)實(shí)現(xiàn)的關(guān)鍵之處在于我們預(yù)先給引擎留出了位置!我們不必因?yàn)閷?duì)引擎的規(guī)則的頻頻變更而制造相當(dāng)多的車身,而是盡可能地沿用和改良現(xiàn)有的車身。
    說到這里,想說一說OO設(shè)計(jì)的一個(gè)誤區(qū)。
    學(xué)習(xí)OO語言的時(shí)候,為了能夠說明“繼承”(或者說“is-a”)這個(gè)概念,教科書上經(jīng)常用實(shí)際生活中的例子來解釋。比如汽車是車,電車是車,F(xiàn)1賽車是汽車,所以車是汽車、電車、F1賽車的上層抽象。這個(gè)例子并沒有錯(cuò)。問題是,這樣的例子過于“形象”了!如果OO設(shè)計(jì)直接就可以將現(xiàn)實(shí)生活中的概念引用過來,那也就不需要什么軟件工程師了!OO設(shè)計(jì)的關(guān)鍵概念是抽象。如果沒有抽象,那所有的軟件工程師的努力都是徒勞的。因?yàn)槿绻麤]有抽象,我們只能去構(gòu)造世界中每一個(gè)對(duì)象。上面這個(gè)例子中,我們應(yīng)該看到“引擎”這個(gè)抽象的存在,因?yàn)檐囮?duì)的工程師們?yōu)樗A(yù)留了位置,為它制定了設(shè)計(jì)規(guī)范。
    上面這個(gè)設(shè)計(jì)也實(shí)現(xiàn)了后面要說的DIP(依賴倒置原則)。但是請(qǐng)記住,OCP是OO設(shè)計(jì)原則中高層次的原則,其余的原則對(duì)OCP提供了不同程度的支持。為了實(shí)現(xiàn)OCP,我們會(huì)自覺或者不自覺地用到其它原則或是諸如Bridge、Decorator等設(shè)計(jì)模式。然而,對(duì)于一個(gè)應(yīng)用系統(tǒng)而言,實(shí)現(xiàn)OCP并不是設(shè)計(jì)目的,我們所希望的只是一個(gè)穩(wěn)定的架構(gòu)。所以對(duì)OCP的追求也應(yīng)該適可而止,不要陷入過渡設(shè)計(jì)。正如Martin本人所說:“No significant program can be 100% closed.”“Closure not complete but strategic”

    (下一篇就要講LSP了,我覺得這是意義最為重要的OO設(shè)計(jì)原則,它直指當(dāng)今主流OO語言的軟肋,點(diǎn)出了OO設(shè)計(jì)的精髓。)

    posted @ 2006-01-18 00:26 GHawk 閱讀(7576) | 評(píng)論 (7)編輯 收藏

    開源 Java 測(cè)試框架(轉(zhuǎn))

    from  http://blog.csdn.net/wangyihust/archive/2006/01/02/568616.aspx

     JUnit   

    JUnit是由 Erich Gamma 和 Kent Beck 編寫的一個(gè)回歸測(cè)試框架(regression testing framework)。Junit測(cè)試是程序員測(cè)試,即所謂白盒測(cè)試,因?yàn)槌绦騿T知道被測(cè)試的軟件如何(How)完成功能和完成什么樣(What)的功能。Junit是一套框架,繼承TestCase類,就可以用Junit進(jìn)行自動(dòng)測(cè)試了。

    http://www.junit.org/

     Cactus   

    Cactus是一個(gè)基于JUnit框架的簡(jiǎn)單測(cè)試框架,用來單元測(cè)試服務(wù)端Java代碼。Cactus框架的主要目標(biāo)是能夠單元測(cè)試服務(wù)端的使用Servlet對(duì)象的Java方法如HttpServletRequest,HttpServletResponse,HttpSession等。

    http://jakarta.apache.org/cactus/

     Abbot   

    Abbot是一個(gè)用來測(cè)試Java GUIs的框架。用簡(jiǎn)單的基于XML的腳本或者Java代碼,你就可以開始一個(gè)GUI。

    http://abbot.sourceforge.net/

     JUnitPerf   

    Junitperf實(shí)際是junit的一個(gè)decorator,通過編寫用于junitperf的單元測(cè)試,我們也可使測(cè)試過程自動(dòng)化。

    http://www.clarkware.com/software/JUnitPerf.html

     DbUnit   

    DbUnit是為數(shù)據(jù)庫(kù)驅(qū)動(dòng)的項(xiàng)目提供的一個(gè)對(duì)JUnit 的擴(kuò)展,除了提供一些常用功能,它可以將你的數(shù)據(jù)庫(kù)置于一個(gè)測(cè)試輪回之間的狀態(tài)。

    http://dbunit.sourceforge.net/

     Mockrunner   

    Mockrunner用在J2EE環(huán)境中進(jìn)行應(yīng)用程序的單元測(cè)試。它不僅支持Struts actions, servlets,過濾器和標(biāo)簽類還包括一個(gè)JDBC和一個(gè)JMS測(cè)試框架,可以用于測(cè)試基于EJB的應(yīng)用程序。

    http://mockrunner.sourceforge.net/index.html

     DBMonster   

    DBMonster是一個(gè)用生成隨機(jī)數(shù)據(jù)來測(cè)試SQL數(shù)據(jù)庫(kù)的壓力測(cè)試工具。

    http://dbmonster.kernelpanic.pl/

     MockEJB   

    MockEJB是一個(gè)不需要EJB容器就能運(yùn)行EJB并進(jìn)行測(cè)試的輕量級(jí)框架。

    http://mockejb.sourceforge.net/

     StrutsTestCase   

    StrutsTestCase 是Junit TestCase類的擴(kuò)展,提供基于Struts框架的代碼測(cè)試。StrutsTestCase同時(shí)提供Mock 對(duì)象方法和Cactus方法用來實(shí)際運(yùn)行Struts ActionServlet,你可以通過運(yùn)行servlet引擎來測(cè)試。因?yàn)镾trutsTestCase使用ActionServlet控制器來測(cè)試你的代碼,因此你不僅可以測(cè)試Action對(duì)象的實(shí)現(xiàn),而且可以測(cè)試mappings,from beans以及forwards聲明。StrutsTestCase不啟動(dòng)servlet容器來測(cè)試struts應(yīng)用程序(容器外測(cè)試)也屬于Mock對(duì)象測(cè)試,但是與EasyMock不同的是,EasyMock是提供了創(chuàng)建Mock對(duì)象的API,而StrutsTest則是專門負(fù)責(zé)測(cè)試Struts應(yīng)用程序的Mock對(duì)象測(cè)試框架。

    http://strutstestcase.sourceforge.net/

     JFCUnit   

    JFCUnit使得你能夠?yàn)镴ava偏移應(yīng)用程序編寫測(cè)試?yán)印K鼮閺挠么a打開的窗口上獲得句柄提供了支持;為在一個(gè)部件層次定位部件提供支持;為在部件中發(fā)起事件(例如按一個(gè)按鈕)以及以線程安全方式處理部件測(cè)試提供支持。

    http://jfcunit.sourceforge.net/

     JTestCase   

    JTestCase 使用XML文件來組織多測(cè)試案例數(shù)據(jù),聲明條件(操作和期望的結(jié)果),提供了一套易于使用的方法來檢索XML中的測(cè)試案例,按照數(shù)據(jù)文件的定義來聲明結(jié)果。

    http://jtestcase.sourceforge.net/

     SQLUnit   

    SQLUnit是一個(gè)單元測(cè)試框架,用于對(duì)數(shù)據(jù)庫(kù)存儲(chǔ)過程進(jìn)行回歸測(cè)試。用 Java/JUnit/XML開發(fā)。

    http://sqlunit.sourceforge.net

     JTR   

    JTR (Java Test Runner)是一個(gè)開源的基于反轉(zhuǎn)控制(IOC)的J2EE測(cè)試框架。它允許你構(gòu)建復(fù)雜的J2EE測(cè)試套件(Test Suites)并連到應(yīng)用服務(wù)器執(zhí)行測(cè)試,可以包括多個(gè)測(cè)試實(shí)例。JTR的licensed是GPL協(xié)議。

    http://jtrunner.sourceforge.net/

     Marathon   

    Marathon是一個(gè)針對(duì)使用Java/Swing開發(fā)GUI應(yīng)用程序的測(cè)試框架,它由recorder, runner 和 editor組成,測(cè)試腳本是python代碼。Marathon的焦點(diǎn)是放在最終用戶的測(cè)試上。

    http://marathonman.sourceforge.net

     TestNG   

    TestNG是根據(jù)JUnit 和 NUnit思想而構(gòu)建的一個(gè)測(cè)試框架,但是TestNG增加了許多新的功能使得它變得更加強(qiáng)大與容易使用比如:
    *支持JSR 175注釋(JDK 1.4利用JavaDoc注釋同樣也支持)
    *靈活的Test配置
    *支持默認(rèn)的runtime和logging JDK功能
    *強(qiáng)大的執(zhí)行模型(不再TestSuite)
    *支持獨(dú)立的測(cè)試方法。

    http://testng.org/

     Surrogate Test framework   

    Surrogate Test framework是一個(gè)值得稱贊單元測(cè)試框架,特別適合于大型,復(fù)雜Java系統(tǒng)的單元測(cè)試。這個(gè)框架能與JUnit,MockEJB和各種支持模擬對(duì)象(mock object )的測(cè)試工具無縫給合。這個(gè)框架基于AspectJ技術(shù)。

    http://surrogate.sourceforge.net

     MockCreator   

    MockCreator可以為給定的interface或class生成模擬對(duì)象(Mock object)的源碼。

    http://mockcreator.sourceforge.net/

     jMock   

    jMock利用mock objects思想來對(duì)Java code進(jìn)行測(cè)試。jMock具有以下特點(diǎn):容易擴(kuò)展,讓你快速簡(jiǎn)單地定義mock objects,因此不必打破程序間的關(guān)聯(lián),讓你定義靈活的超越對(duì)象之間交互作用而帶來測(cè)試局限,減少你測(cè)試地脆弱性。

    http://www.jmock.org/

     EasyMock   

    EasyMock為Mock Objects提供接口并在JUnit測(cè)試中利用Java的proxy設(shè)計(jì)模式生成它們的實(shí)例。EasyMock最適合于測(cè)試驅(qū)動(dòng)開發(fā)。

    http://www.easymock.org/

     The Grinder   

    The Grinder是一個(gè)負(fù)載測(cè)試框架。在BSD開源協(xié)議下免費(fèi)使用。

    http://grinder.sourceforge.net/

     XMLUnit   

    XMLUnit不僅有Java版本的還有.Net版本的。Java開發(fā)的XMLUnit提供了兩個(gè)JUnit 擴(kuò)展類XMLAssert和XMLTestCase,和一組支持的類。這些類可以用來比較兩張XML之間的不同之處,展示XML利用XSLT來,校驗(yàn)XML,求得XPath表達(dá)式在XML中的值,遍歷XML中的某一節(jié)點(diǎn)利DOM展開。

    http://xmlunit.sourceforge.net/

     Jameleon   

    Jameleon一個(gè)自動(dòng)化測(cè)試工具。它被用來測(cè)試各種各樣的應(yīng)用程序,所以它被設(shè)計(jì)成插件模式。為了使整個(gè)測(cè)試過程變得簡(jiǎn)單Jameleon提供了一個(gè)GUI,因此Jameleon實(shí)現(xiàn)了一個(gè)Swing 插件。

    http://jameleon.sourceforge.net/index.html

     J2MEUnit   

    J2MEUnit是應(yīng)用在J2ME應(yīng)用程序的一個(gè)單元測(cè)試框架。它基于JUnit。

    http://j2meunit.sourceforge.net/

     Jetif   

    Jetif是一個(gè)用純Java實(shí)現(xiàn)的回歸測(cè)試框架。它為Java程序單元測(cè)試以及功能測(cè)試提供了一個(gè)簡(jiǎn)單而且可 伸縮的架構(gòu),可以用于個(gè)人開發(fā)或企業(yè)級(jí)開發(fā)的測(cè)試。它容易使用,功能強(qiáng)大,而且擁有一些企業(yè)級(jí)測(cè)試的重要功能。Jetif來源于JUnit, JTestCase以及TestNG的啟發(fā),有幾個(gè)基本的概念直接來自于JUnit, 比如說斷言機(jī)制,Test Listener的概念,因此從JUnit轉(zhuǎn)到Jetif是非常容易的。

    http://jetif.sourceforge.net/

     GroboUtils   

    GroboUtils使得擴(kuò)展Java測(cè)試變得可能。它包括用在Java不同方面測(cè)試的多個(gè)子項(xiàng)目。在GroboUtils中最常被到的工具是:多線程測(cè)試(multi-threaded tests),整體單元測(cè)試(hierarchial unit tests),代碼覆蓋工具(code coverage tool)。

    http://groboutils.sourceforge.net/

     Testare   

    TESTARE是用來簡(jiǎn)化分布式應(yīng)用程序(比如:在SERVLETS,JMS listeners, CORBA ORBs或RMI環(huán)境下)測(cè)試開發(fā)過程的一個(gè)測(cè)試框架。

    https://testare.dev.java.net/

    posted @ 2006-01-10 10:41 GHawk 閱讀(1247) | 評(píng)論 (0)編輯 收藏

    敏捷軟件開發(fā) 讀書筆記 (2)——OO五大原則(1.SRP 單一職責(zé)原則)

          一點(diǎn)說明:OO的五大原則是指SRP、OCP、LSP、DIP、ISP。這五個(gè)原則是書中所提到的。除此之外,書中還提到一些高層次的原則用于組織高層的設(shè)計(jì)元素,這些放到下次再寫。當(dāng)然,OO設(shè)計(jì)的原則可能不止這五個(gè),希望大家多提寶貴意見,多多交流。

          在學(xué)習(xí)和使用OO設(shè)計(jì)的時(shí)候,我們應(yīng)該明白:OO的出現(xiàn)使得軟件工程師們能夠用更接近真實(shí)世界的方法描述軟件系統(tǒng)。然而,軟件畢竟是建立在抽象層次上的東西,再怎么接近真實(shí),也不能替代真實(shí)或被真實(shí)替代。

          OO設(shè)計(jì)的五大原則之間并不是相互孤立的。彼此間存在著一定關(guān)聯(lián),一個(gè)可以是另一個(gè)原則的加強(qiáng)或是基礎(chǔ)。違反其中的某一個(gè),可能同時(shí)違反了其余的原則。因此應(yīng)該把這些原則融會(huì)貫通,牢記在心!

    1. SRP(Single Responsibility Principle 單一職責(zé)原則)
          單一職責(zé)很容易理解,也很容易實(shí)現(xiàn)。所謂單一職責(zé),就是一個(gè)設(shè)計(jì)元素只做一件事。什么是“只做一件事”?簡(jiǎn)單說就是少管閑事。現(xiàn)實(shí)中就是如此,如果要你專心做一件事情,任何人都有信心可以做得很出色。但如果,你整天被亂七八糟的事所累,還有心思和精力把每件事都作好么?
    fig-1.JPG
         “單一職責(zé)”就是要在設(shè)計(jì)中為每種職責(zé)設(shè)計(jì)一個(gè)類,彼此保持正交,互不干涉。這個(gè)雕塑(二重奏)就是正交的一個(gè)例子,鋼琴家和小提琴家各自演奏自己的樂譜,而結(jié)果就是一個(gè)和諧的交響樂。當(dāng)然,真實(shí)世界中,演奏小提琴和彈鋼琴的必須是兩個(gè)人,但是在軟件中,我們往往會(huì)把兩者甚至更多攪和到一起,很多時(shí)候只是為了方便或是最初設(shè)計(jì)的時(shí)候沒有想到。 

          這樣的例子在設(shè)計(jì)中很常見,書中就給了一個(gè)很好的例子:調(diào)制解調(diào)器。這是一個(gè)調(diào)制解調(diào)器最基本的功能。但是這個(gè)類事實(shí)上完成了兩個(gè)職責(zé):連接的建立和中斷、數(shù)據(jù)的發(fā)送和接收。顯然,這違反了SRP。這樣做會(huì)有潛在的問題:當(dāng)僅需要改變數(shù)據(jù)連接方式時(shí),必須修改Modem類,而修改Modem類的結(jié)果就是使得任何依賴Modem類的元素都需要重新編譯,不管它是不是用到了數(shù)據(jù)連接功能。解決的辦法,書中也已經(jīng)給出:重構(gòu)Modem類,從中抽出兩個(gè)接口,一個(gè)專門負(fù)責(zé)連接、另一個(gè)專門負(fù)責(zé)數(shù)據(jù)發(fā)送。依賴Modem類的元素也要做相應(yīng)的細(xì)化,根據(jù)職責(zé)的不同分別依賴不同的接口。最后由ModemImplementation類實(shí)現(xiàn)這兩個(gè)接口。
    fig-2.JPG

          從這個(gè)例子中,我們不難發(fā)現(xiàn),違反SRP通常是由于過于“真實(shí)”地設(shè)計(jì)了一個(gè)類所造成的。因此,解決辦法是往更高一層進(jìn)行抽象化提取,將對(duì)某個(gè)具體類的依賴改變?yōu)閷?duì)一組接口或抽象類的依賴。當(dāng)然,這個(gè)抽象化的提取應(yīng)該根據(jù)需要設(shè)計(jì),而不是盲目提取。比如剛才這個(gè)Modem的例子中,如果有必要,還可以把DataChannel抽象為DataSender和DataReceiver兩個(gè)接口。
     

    posted @ 2006-01-09 21:17 GHawk 閱讀(5557) | 評(píng)論 (5)編輯 收藏

    敏捷軟件開發(fā) 讀書筆記 (1)——設(shè)計(jì)的目標(biāo)

    軟件設(shè)計(jì)是一種抽象活動(dòng),設(shè)計(jì)所要實(shí)現(xiàn)的是產(chǎn)出代碼。就這一點(diǎn)來說,任何人都會(huì)設(shè)計(jì)。但是,正如我們?nèi)粘I钪兴勀慷没蛴H身經(jīng)歷,設(shè)計(jì)有優(yōu)劣之分。

    從項(xiàng)目管理的角度去理解,設(shè)計(jì)是為了滿足涉眾(Stakeholders)的需求。顯然,一個(gè)設(shè)計(jì)應(yīng)該滿足客戶對(duì)系統(tǒng)的功能及非功能需求。但單是滿足了這一點(diǎn),并不能稱為一個(gè)好的設(shè)計(jì)。因?yàn)殚_發(fā)者同樣屬于涉眾!而開發(fā)者的需求又是怎樣的呢?至少,應(yīng)該有以下幾條吧:

    • 老板希望軟件交付后,不應(yīng)該有很高的維護(hù)成本。如果開發(fā)人員為了維護(hù)而經(jīng)常出差或者加班且久久不能投入新項(xiàng)目,顯然,換了誰是老板都不愿意這種事情發(fā)生。
    • 開發(fā)人員呢?誰愿意放棄和家人朋友而拼死拼活在單位加班,總是有這么多麻煩事纏著你,煩不煩哪!
    • ……等等

    所以,設(shè)計(jì)應(yīng)該盡可能多地照顧到維護(hù)和變更。

    為了兼顧各戶滿意和維護(hù)成本,設(shè)計(jì)應(yīng)該不斷挑戰(zhàn)其終極目標(biāo)——松耦合。不管是XP或UP,這個(gè)目標(biāo)都不會(huì)改變。OO設(shè)計(jì)中的五大原則,其根本目的就是降低組件間的耦合度,避免牽一發(fā)則動(dòng)全身的現(xiàn)象發(fā)生。降低耦合度不僅能夠提高軟件內(nèi)在的質(zhì)量,還能大大減少不必要的編譯時(shí)間、減少向版本控制系統(tǒng)提交源碼的網(wǎng)絡(luò)開銷……

    如何鑒別設(shè)計(jì)的這一指標(biāo)?軟件工程中有專用的度量:CBO(Coupling Between Objects),那是由公式計(jì)算出來的,也有很多工具支持,值得一試。(聽過幾次李維先生的講座,他經(jīng)常拿Together的度量功能炫耀^_^)

    但是,作為一個(gè)開發(fā)人員,對(duì)手中的代碼應(yīng)該有適當(dāng)?shù)拿舾行浴.吘梗@些代碼是你親手創(chuàng)造的,誰不希望自己的作品得到眾人的贊許?或許能換得一次加薪升職的機(jī)會(huì)^_^ 退一步,這可關(guān)系到寶貴的休息時(shí)間啊。所以,開發(fā)者應(yīng)該對(duì)自己的產(chǎn)品有這樣一種意識(shí):及時(shí)修正設(shè)計(jì)中不合理的地方。

    敏捷過程告訴我們:在代碼“有味道”的時(shí)候進(jìn)行重構(gòu)。“有味道”是代碼正在變質(zhì)的標(biāo)志,很遺憾,能夠使代碼保持原味的防腐劑還沒發(fā)明。為了保證代碼質(zhì)量,及時(shí)重構(gòu)是必要的。這就像在燒烤的時(shí)候?yàn)榱朔乐箍窘梗愕米跔t子前經(jīng)常翻動(dòng)肉塊一樣。

    如何聞出代碼的味道?認(rèn)真學(xué)習(xí)一下OO吧,別以為OO很簡(jiǎn)單,就是繼承+封裝+多態(tài),誰都會(huì)。即使是書中記述的五大原則,想要運(yùn)用自如,也得多感覺感覺才行。很多時(shí)候,我們不知不覺就把蛆蟲放進(jìn)了代碼中……

    好了,下一篇:OO五大原則

    posted @ 2006-01-06 18:17 GHawk 閱讀(1596) | 評(píng)論 (3)編輯 收藏

    敏捷軟件開發(fā) 讀書筆記 (序——廢話)

    7-5083-1503-0l.gif

    最近正在讀這本書,喜歡影印版,是因?yàn)闀衅恋牟鍒D。:)慚愧,如此的好書到現(xiàn)在才去讀。
    準(zhǔn)備邊讀邊記錄些心得,今天先說些廢話。:P

    先粗略地概覽了一遍全書。本書主要分以下幾個(gè)部分:

    1. 敏捷軟件過程。主要以XP為例。這部分的最后一章,用一個(gè)對(duì)話式的小故事講述了一個(gè)非常小的過程。給了讀者關(guān)于敏捷過程的形象化的認(rèn)識(shí)。
    2. 敏捷設(shè)計(jì)。這部分是個(gè)很大的看點(diǎn)。它講述了設(shè)計(jì)中一些常見的問題,及其應(yīng)對(duì)(用幾個(gè)經(jīng)典的設(shè)計(jì)原則)。
    3. 案例實(shí)踐。講述了如何利用設(shè)計(jì)模式去實(shí)踐第二部分中提到的設(shè)計(jì)原則和避免設(shè)計(jì)中的“味道”。

    之所以覺得這本書好,還與一個(gè)人有關(guān)。就是交大軟件學(xué)院的林德彰老師。林先生的課,風(fēng)趣幽默,能夠用直觀形象的語言讓學(xué)生對(duì)講課內(nèi)容產(chǎn)生深刻的印象。(我可不是托兒,網(wǎng)上能搜到些林先生講課的片斷,要是懷疑,可以驗(yàn)證一番)。記得在軟件工程這門課里,林先生給我們講了很多有關(guān)設(shè)計(jì)原則的內(nèi)容,其中就有“開閉原則(OCP)”、“里氏替換原則(LSP)”等……就把這本書當(dāng)作是一本補(bǔ)充讀物吧。

    言歸正傳。個(gè)人感覺這本書的總體風(fēng)格,就和所要講的“敏捷”一樣,并不帶著厚重的學(xué)院派風(fēng)味,而是更注重實(shí)踐。并不是沒有理論,只是把理論融入到了實(shí)踐中,簡(jiǎn)化了理論的復(fù)雜性。讀起來感覺很帶勁兒。

    廢話說到這里,下一步的計(jì)劃就是跟著自己的進(jìn)度寫讀書心得了。我想把對(duì)書中內(nèi)容的理解和以前在林先生的課上所學(xué)的結(jié)合在一起,導(dǎo)出閱讀此書時(shí)的大腦活動(dòng)鏡像。

    posted @ 2005-12-27 12:00 GHawk 閱讀(1611) | 評(píng)論 (4)編輯 收藏

    一個(gè)介紹Java開源項(xiàng)目及工具的網(wǎng)站

    按功能進(jìn)行了歸類
    http://java-source.net


    再追加一個(gè)中文的
    http://www.open-open.com

    posted @ 2005-12-20 13:45 GHawk 閱讀(511) | 評(píng)論 (1)編輯 收藏

    用 Cobertura 測(cè)量測(cè)試覆蓋率

    http://www-128.ibm.com/developerworks/cn/java/j-cobertura/

    用 Cobertura 測(cè)量測(cè)試覆蓋率

    找出隱藏 bug 的未測(cè)試到的代碼

    developerWorks
    文檔選項(xiàng)
    將此頁(yè)作為電子郵件發(fā)送

    將此頁(yè)作為電子郵件發(fā)送

    未顯示需要 JavaScript 的文檔選項(xiàng)


    對(duì)此頁(yè)的評(píng)價(jià)

    幫助我們改進(jìn)這些內(nèi)容


    級(jí)別: 初級(jí)

    Elliotte Rusty Harold, 副教授, Polytechnic University

    2005 年 5 月 26 日

    Cobertura 是一種開源工具,它通過檢測(cè)基本的代碼,并觀察在測(cè)試包運(yùn)行時(shí)執(zhí)行了哪些代碼和沒有執(zhí)行哪些代碼,來測(cè)量測(cè)試覆蓋率。除了找出未測(cè)試到的代碼并發(fā)現(xiàn) bug 外,Cobertura 還可以通過標(biāo)記無用的、執(zhí)行不到的代碼來優(yōu)化代碼,還可以提供 API 實(shí)際操作的內(nèi)部信息。Elliotte Rusty Harold 將與您分享如何利用代碼覆蓋率的最佳實(shí)踐來使用 Cobertura。

    盡管測(cè)試先行編程(test-first programming)和單元測(cè)試已不能算是新概念,但測(cè)試驅(qū)動(dòng)的開發(fā)仍然是過去 10 年中最重要的編程創(chuàng)新。最好的一些編程人員在過去半個(gè)世紀(jì)中一直在使用這些技術(shù),不過,只是在最近幾年,這些技術(shù)才被廣泛地視為在時(shí)間及成本預(yù)算內(nèi)開發(fā)健壯的無缺陷軟件的關(guān)鍵所在。但是,測(cè)試驅(qū)動(dòng)的開發(fā)不能超過測(cè)試所能達(dá)到的程度。測(cè)試改進(jìn)了代碼質(zhì)量,但這也只是針對(duì)實(shí)際測(cè)試到的那部分代碼而言的。您需要有一個(gè)工具告訴您程序的哪些部分沒有測(cè)試到,這樣就可以針對(duì)這些部分編寫測(cè)試代碼并找出更多 bug。

    Mark Doliner 的 Cobertura (cobertura 在西班牙語是覆蓋的意思)是完成這項(xiàng)任務(wù)的一個(gè)免費(fèi) GPL 工具。Cobertura 通過用額外的語句記錄在執(zhí)行測(cè)試包時(shí),哪些行被測(cè)試到、哪些行沒有被測(cè)試到,通過這種方式來度量字節(jié)碼,以便對(duì)測(cè)試進(jìn)行監(jiān)視。然后它生成一個(gè) HTML 或者 XML 格式的報(bào)告,指出代碼中的哪些包、哪些類、哪些方法和哪些行沒有測(cè)試到。可以針對(duì)這些特定的區(qū)域編寫更多的測(cè)試代碼,以發(fā)現(xiàn)所有隱藏的 bug。

    閱讀 Cobertura 輸出

    我們首先查看生成的 Cobertura 輸出。圖 1 顯示了對(duì) Jaxen 測(cè)試包運(yùn)行 Cobertura 生成的報(bào)告(請(qǐng)參閱 參考資料)。從該報(bào)告中,可以看到從很好(在 org.jaxen.expr.iter 包中幾乎是 100%)到極差(在 org.jaxen.dom.html 中完全沒有覆蓋)的覆蓋率結(jié)果。


    圖 1. Jaxen 的包級(jí)別覆蓋率統(tǒng)計(jì)數(shù)據(jù)

    Cobertura 通過被測(cè)試的行數(shù)和被測(cè)試的分支數(shù)來計(jì)算覆蓋率。第一次測(cè)試時(shí),兩種測(cè)試方法之間的差別并不是很重要。Cobertura 還為類計(jì)算平均 McCabe 復(fù)雜度(請(qǐng)參閱 參考資料)。

    可以深入挖掘 HTML 報(bào)告,了解特定包或者類的覆蓋率。圖 2 顯示了 org.jaxen.function 包的覆蓋率統(tǒng)計(jì)。在這個(gè)包中,覆蓋率的范圍從 SumFunction 類的 100% 到 IdFunction 類的僅為 5%。


    圖 2. org.jaxen.function 包中的代碼覆蓋率

    進(jìn)一步深入到單獨(dú)的類中,具體查看哪一行代碼沒有測(cè)試到。圖 3 顯示了 NameFunction 類中的部分覆蓋率。最左邊一欄顯示行號(hào)。后一欄顯示了執(zhí)行測(cè)試時(shí)這一行被執(zhí)行的次數(shù)。可以看出,第 112 行被執(zhí)行了 100 次,第 114 行被執(zhí)行了 28 次。用紅色突出顯示的那些行則根本沒有測(cè)試到。這個(gè)報(bào)告表明,雖然從總體上說該方法被測(cè)試到了,但實(shí)際上還有許多分支沒有測(cè)試到。


    圖 3. NameFunction 類中的代碼覆蓋率

    Cobertura 是 jcoverage 的分支(請(qǐng)參閱 參考資料)。GPL 版本的 jcoverage 已經(jīng)有一年沒有更新過了,并且有一些長(zhǎng)期存在的 bug,Cobertura 修復(fù)了這些 bug。原來的那些 jcoverage 開發(fā)人員不再繼續(xù)開發(fā)開放源碼,他們轉(zhuǎn)向開發(fā) jcoverage 的商業(yè)版和 jcoverage+,jcoverage+ 是一個(gè)從同一代碼基礎(chǔ)中發(fā)展出來的封閉源代碼產(chǎn)品。開放源碼的奇妙之處在于:一個(gè)產(chǎn)品不會(huì)因?yàn)樵_發(fā)人員決定讓他們的工作獲得相應(yīng)的報(bào)酬而消亡。

    確認(rèn)遺漏的測(cè)試

    利用 Cobertura 報(bào)告,可以找出代碼中未測(cè)試的部分并針對(duì)它們編寫測(cè)試。例如,圖 3 顯示 Jaxen 需要進(jìn)行一些測(cè)試,運(yùn)用 name() 函數(shù)對(duì)文字節(jié)點(diǎn)、注釋節(jié)點(diǎn)、處理指令節(jié)點(diǎn)、屬性節(jié)點(diǎn)和名稱空間節(jié)點(diǎn)進(jìn)行測(cè)試。

    如果有許多未覆蓋的代碼,像 Cobertura 在這里報(bào)告的那樣,那么添加所有缺少的測(cè)試將會(huì)非常耗時(shí),但也是值得的。不一定要一次完成它。您可以從被測(cè)試的最少的代碼開始,比如那些所有沒有覆蓋的包。在測(cè)試所有的包之后,就可以對(duì)每一個(gè)顯示為沒有覆蓋的類編寫一些測(cè)試代碼。對(duì)所有類進(jìn)行專門測(cè)試后,還要為所有未覆蓋的方法編寫測(cè)試代碼。在測(cè)試所有方法之后,就可以開始分析對(duì)未測(cè)試的語句進(jìn)行測(cè)試的必要性。

    (幾乎)不留下任何未測(cè)試的代碼

    是否有一些可以測(cè)試但不應(yīng)測(cè)試的內(nèi)容?這取決于您問的是誰。在 JUnit FAQ 中,J. B. Rainsberger 寫到“一般的看法是:如果 自身 不會(huì)出問題,那么它會(huì)因?yàn)樘?jiǎn)單而不會(huì)出問題。第一個(gè)例子是 getX() 方法。假定 getX() 方法只提供某一實(shí)例變量的值。在這種情況下,除非編譯器或者解釋器出了問題,否則 getX() 是不會(huì)出問題的。因此,不用測(cè)試 getX(),測(cè)試它不會(huì)帶來任何好處。對(duì)于 setX() 方法來說也是如此,不過,如果 setX() 方法確實(shí)要進(jìn)行任何參數(shù)驗(yàn)證,或者說確實(shí)有副作用,那么還是有必要對(duì)其進(jìn)行測(cè)試。”

    理論上,對(duì)未覆蓋的代碼編寫測(cè)試代碼不一定就會(huì)發(fā)現(xiàn) bug。但在實(shí)踐中,我從來沒有碰到?jīng)]有發(fā)現(xiàn) bug 的情況。未測(cè)試的代碼充滿了 bug。所做的測(cè)試越少,在代碼中隱藏的、未發(fā)現(xiàn)的 bug 就會(huì)越多。

    我不同意。我已經(jīng)記不清在“簡(jiǎn)單得不會(huì)出問題”的代碼中發(fā)現(xiàn)的 bug 的數(shù)量了。確實(shí),一些 getter 和 setter 很簡(jiǎn)單,不可能出問題。但是我從來就沒有辦法區(qū)分哪些方法是真的簡(jiǎn)單得不會(huì)出錯(cuò),哪些方法只是看上去如此。編寫覆蓋像 setter 和 getter 這樣簡(jiǎn)單方法的測(cè)試代碼并不難。為此所花的少量時(shí)間會(huì)因?yàn)樵谶@些方法中發(fā)現(xiàn)未曾預(yù)料到的 bug 而得到補(bǔ)償。

    一般來說,開始測(cè)量后,達(dá)到 90% 的測(cè)試覆蓋率是很容易的。將覆蓋率提高到 95% 或者更高就需要?jiǎng)右幌履X筋。例如,可能需要裝載不同版本的支持庫(kù),以測(cè)試沒有在所有版本的庫(kù)中出現(xiàn)的 bug。或者需要重新構(gòu)建代碼,以便測(cè)試通常執(zhí)行不到的部分代碼。可以對(duì)類進(jìn)行擴(kuò)展,讓它們的受保護(hù)方法變?yōu)楣卜椒ǎ@樣就可以對(duì)這些方法進(jìn)行測(cè)試。這些技巧看起來像是多此一舉,但是它們?cè)鴰椭以谝话氲臅r(shí)間內(nèi)發(fā)現(xiàn)更多的未發(fā)現(xiàn)的 bug。

    并不總是可以得到完美的、100% 的代碼覆蓋率。有時(shí)您會(huì)發(fā)現(xiàn),不管對(duì)代碼如何改造,仍然有一些行、方法、甚至是整個(gè)類是測(cè)試不到的。下面是您可能會(huì)遇到的挑戰(zhàn)的一些例子:

    • 只在特定平臺(tái)上執(zhí)行的代碼。例如,在一個(gè)設(shè)計(jì)良好的 GUI 應(yīng)用程序中,添加一個(gè) Exit 菜單項(xiàng)的代碼可以在 Windows PC 上運(yùn)行,但它不能在 Mac 機(jī)上運(yùn)行。

    • 捕獲不會(huì)發(fā)生的異常的 catch 語句,比如在從 ByteArrayInputStream 進(jìn)行讀取操作時(shí)拋出的 IOException

    • 非公共類中的一些方法,它們永遠(yuǎn)也不會(huì)被實(shí)際調(diào)用,只是為了滿足某個(gè)接口契約而必須實(shí)現(xiàn)。

    • 處理虛擬機(jī) bug 的代碼塊,比如說,不能識(shí)別 UTF-8 編碼。

    考慮到上面這些以及類似的情況,我認(rèn)為一些極限程序員自動(dòng)刪除所有未測(cè)試代碼的做法是不切實(shí)際的,并且可能具有一定的諷刺性。不能總是獲得絕對(duì)完美的測(cè)試覆蓋率并不意味著就不會(huì)有更好的覆蓋率。

    然而,比執(zhí)行不到的語句和方法更常見的是殘留代碼,它不再有任何作用,并且從代碼基中去掉這些代碼也不會(huì)產(chǎn)生任何影響。有時(shí)可以通過使用反射來訪問私有成員這樣的怪招來測(cè)試未測(cè)試的代碼。還可以為未測(cè)試的、包保護(hù)(package-protected)的代碼來編寫測(cè)試代碼,將測(cè)試類放到將要測(cè)試的類所在那個(gè)包中。但最好不要這樣做。所有不能通過發(fā)布的(公共的和受保護(hù)的)接口訪問的代碼都應(yīng)刪除。執(zhí)行不到的代碼不應(yīng)當(dāng)成為代碼基的一部分。代碼基越小,它就越容易被理解和維護(hù)。

    不要漏掉測(cè)量單元測(cè)試包和類本身。我不止一次注意到,某些個(gè)測(cè)試方法或者類沒有被測(cè)試包真正運(yùn)行。通常這表明名稱規(guī)范中存在問題(比如將一個(gè)方法命名為 tesSomeReallyComplexCondition,而不是將其命名為 testSomeReallyComplexCondition),或者忘記將一個(gè)類添加到主 suite() 方法中。在其他情況下,未預(yù)期的條件導(dǎo)致跳過了測(cè)試方法中的代碼。不管是什么情況,都是雖然已經(jīng)編寫了測(cè)試代碼,但沒有真正運(yùn)行它。JUnit 不會(huì)告訴您它沒有像您所想的那樣運(yùn)行所有測(cè)試,但是 Cobertura 會(huì)告訴您。找出了未運(yùn)行的測(cè)試后,改正它一般很容易。



    回頁(yè)首


    運(yùn)行 Cobertura

    在了解了測(cè)量代碼覆蓋率的好處后,讓我們?cè)賮碛懻撘幌氯绾斡?Cobertura 測(cè)量代碼覆蓋率的具體細(xì)節(jié)。Cobertura 被設(shè)計(jì)成為在 Ant 中運(yùn)行。現(xiàn)在還沒有這方面的 IDE 插件可用,不過一兩年內(nèi)也許就會(huì)有了。

    首先需要在 build.xml 文件中添加一個(gè)任務(wù)定義。以下這個(gè)頂級(jí) taskdef 元素將 cobertura.jar 文件限定在當(dāng)前工作目錄中:

    <taskdef classpath="cobertura.jar" resource="tasks.properties" />

    然后,需要一個(gè) cobertura-instrument 任務(wù),該任務(wù)將在已經(jīng)編譯好的類文件中添加日志代碼。todir 屬性指定將測(cè)量類放到什么地方。fileset 子元素指定測(cè)量哪些 .class 文件:

    <target name="instrument">
      <cobertura-instrument todir="target/instrumented-classes">
        <fileset dir="target/classes">
          <include name="**/*.class"/>
        </fileset>
      </cobertura-instrument>
    </target>

    用通常運(yùn)行測(cè)試包的同一種類型的 Ant 任務(wù)運(yùn)行測(cè)試。惟一的區(qū)別在于:被測(cè)量的類必須在原始類出現(xiàn)在類路徑中之前出現(xiàn)在類路徑中,而且需要將 Cobertura JAR 文件添加到類路徑中:

    <target name="cover-test" depends="instrument">
      <mkdir dir="${testreportdir}" />
      <junit dir="./" failureproperty="test.failure" printSummary="yes" 
             fork="true" haltonerror="true">
        <!-- Normally you can create this task by copying your existing JUnit
             target, changing its name, and adding these next two lines.
             You may need to change the locations to point to wherever 
             you've put the cobertura.jar file and the instrumented classes. -->
        <classpath location="cobertura.jar"/>
        <classpath location="target/instrumented-classes"/>
        <classpath>
          <fileset dir="${libdir}">
            <include name="*.jar" />
          </fileset>
          <pathelement path="${testclassesdir}" />
          <pathelement path="${classesdir}" />
        </classpath>
        <batchtest todir="${testreportdir}">
          <fileset dir="src/java/test">
            <include name="**/*Test.java" />
            <include name="org/jaxen/javabean/*Test.java" />
          </fileset>
        </batchtest>
      </junit>
    </target>>

    Jaxen 項(xiàng)目使用 JUnit 作為其測(cè)試框架,但是 Cobertura 是不受框架影響的。它在 TestNG、Artima SuiteRunner、HTTPUni 或者在您自己在地下室開發(fā)的系統(tǒng)中一樣工作得很好。

    最后,cobertura-report 任務(wù)生成本文開始部分看到的那個(gè) HTML 文件:

    <target name="coverage-report" depends="cover-test">
     <cobertura-report srcdir="src/java/main" destdir="cobertura"/>
    </target>

    srcdir 屬性指定原始的 .java 源代碼在什么地方。destdir 屬性指定 Cobertura 放置輸出 HTML 的那個(gè)目錄的名稱。

    在自己的 Ant 編譯文件中加入了類似的任務(wù)后,就可以通過鍵入以下命令來生成一個(gè)覆蓋報(bào)告:

    % ant instrument
    % ant cover-test
    % ant coverage-report

    當(dāng)然,如果您愿意的話,還可以改變目標(biāo)任務(wù)的名稱,或者將這三項(xiàng)任務(wù)合并為一個(gè)目標(biāo)任務(wù)。



    回頁(yè)首


    結(jié)束語

    Cobertura 是敏捷程序員工具箱中新增的一個(gè)重要工具。通過生成代碼覆蓋率的具體數(shù)值,Cobertura 將單元測(cè)試從一種藝術(shù)轉(zhuǎn)變?yōu)橐婚T科學(xué)。它可以尋找測(cè)試覆蓋中的空隙,直接找到 bug。測(cè)量代碼覆蓋率使您可以獲得尋找并修復(fù) bug 所需的信息,從而開發(fā)出對(duì)每個(gè)人來說都更健壯的軟件。



    回頁(yè)首


    參考資料



    回頁(yè)首


    關(guān)于作者

    Elliotte Rusty Harold 出生在新奧爾良,現(xiàn)在他還定期回老家喝一碗美味的秋葵湯。不過目前,他和妻子 Beth 定居在紐約臨近布魯克林的 Prospect Heights,同住的還有他的貓咪 Charm(取自夸克)和 Marjorie(取自他岳母的名字)。他是 Polytechnic 大學(xué)計(jì)算機(jī)科學(xué)的副教授,講授 Java 技術(shù)和面向?qū)ο缶幊獭K?Cafe au Lait 網(wǎng)站是 Internet 上最受歡迎的獨(dú)立 Java 站點(diǎn)之一,姊妹站點(diǎn) Cafe con Leche 已經(jīng)成為最受歡迎的 XML 站點(diǎn)之一。他的著作包括 Effective XML Processing XML with Java Java Network Programming The XML 1.1 Bible 。目前他正在從事 XML 的 XOM API、Jaxen XPath 引擎和 Jester 測(cè)試覆蓋率工具的開發(fā)工作。


    posted @ 2005-12-17 11:23 GHawk 閱讀(423) | 評(píng)論 (0)編輯 收藏

    EasyMock 2.0_ReleaseCandidate 文檔翻譯

         摘要: EasyMock 2.0_ReleaseCandidate Readme Documentation for release 2.0_ReleaseCandidate (October 15 2005)? 2001-2005 OFFIS, Tammo Freese. 翻譯:GHawk, 2005-12-15 EasyMock 2 is a library that provides an ...  閱讀全文

    posted @ 2005-12-15 16:06 GHawk 閱讀(4763) | 評(píng)論 (2)編輯 收藏

    過程的代價(jià)

    這個(gè)月剛進(jìn)入公司,加入了一個(gè)10人左右的團(tuán)隊(duì),用Java做一個(gè)網(wǎng)站后臺(tái)。

    客戶是日本公司,他們做了項(xiàng)目的大部分分析(Requirements, Use cases, Domain model...)。我們負(fù)責(zé)的是詳細(xì)設(shè)計(jì)和開發(fā)。我是項(xiàng)目開始幾星期后才進(jìn)的公司。Schedule也已經(jīng)為我分配好了。大家都按照schedule上的安排工作著。

    上星期開會(huì)的時(shí)候得知,日本這次采用的是agile過程。而我們的schedule更類似于RUP這樣的過程。RUP這個(gè)學(xué)院派和Agile這個(gè)造反派狹路相逢,問題也就出現(xiàn)了。

    大家工作都很賣力,為了能按進(jìn)度提交制品,有時(shí)還通宵達(dá)旦解決問題。我們這支團(tuán)隊(duì)的戰(zhàn)斗力和信心是不容懷疑的。可是大家努力的結(jié)果換來的卻是用戶的抱怨。大家都困惑不解。問題究竟出在哪兒?

    日方在項(xiàng)目中強(qiáng)調(diào)的是Agile過程,我們采用的則是傳統(tǒng)的過程。一開始,兩個(gè)過程方法之間的差異并不大;對(duì)我們提交的制品,客戶也沒有什么異議。但是,直到客戶提出問題之前,我們所提交的制品都是一些設(shè)計(jì)文檔。而我們的制品也僅限于此——沒有一個(gè)可用的EAR包、沒有寫過 test case。很明顯,我們犯了agile的大忌。

    Agile所強(qiáng)調(diào)的是快速的構(gòu)建、輕量級(jí)的迭代、TDD等。由于之前沒有寫test case,詳細(xì)設(shè)計(jì)也沒有test case可以參照。設(shè)計(jì)本身是不是合理,是不是testable也不得而知。致使在設(shè)計(jì)test case的時(shí)候無從下手,很多類甚至都沒有辦法測(cè)試。整個(gè)架構(gòu)的可行性很難估算。

    往后考慮。一次大規(guī)模的重構(gòu)可能是少不了的。雖然agile過程本身提倡以TDD為基礎(chǔ)的重構(gòu)。但是現(xiàn)在的重構(gòu)可能造成的代價(jià)已經(jīng)不是一次輕量級(jí)的增量迭代了。

    說到這里,總結(jié)幾點(diǎn),希望能在以后的工作中引起注意:
    1. Agile很難管理,項(xiàng)目早期應(yīng)該對(duì)各種風(fēng)險(xiǎn)有盡可能全面的評(píng)估,schedule的設(shè)置中應(yīng)該定義好 test case 和 build 的時(shí)間點(diǎn)。
    2. 設(shè)計(jì)不必太詳細(xì),用頻繁的測(cè)試和重構(gòu)完善設(shè)計(jì)。
    3. Test case 優(yōu)先設(shè)計(jì),這樣在架構(gòu)中就會(huì)對(duì)testability有足夠多的考慮。
    4. 團(tuán)隊(duì)內(nèi)部對(duì)共同的難題應(yīng)該及早進(jìn)行討論和解決,問題的解決方案應(yīng)該傳遞到每個(gè)組員,盡可能保證團(tuán)隊(duì)的能力同步。

    posted @ 2005-12-14 09:57 GHawk 閱讀(1200) | 評(píng)論 (5)編輯 收藏

    EJB 異常處理的最佳做法

    http://www-128.ibm.com/developerworks/cn/java/j-ejbexcept/

    EJB 異常處理的最佳做法

    學(xué)習(xí)在基于 EJB 的系統(tǒng)上編寫可以更快解決問題的代碼

    developerWorks
    文檔選項(xiàng)
    將此頁(yè)作為電子郵件發(fā)送

    將此頁(yè)作為電子郵件發(fā)送

    未顯示需要 JavaScript 的文檔選項(xiàng)


    對(duì)此頁(yè)的評(píng)價(jià)

    幫助我們改進(jìn)這些內(nèi)容


    級(jí)別: 初級(jí)

    Srikanth Shenoy, J2EE 顧問

    2002 年 5 月 05 日

    隨著 J2EE 成為企業(yè)開發(fā)平臺(tái)之選,越來越多基于 J2EE 的應(yīng)用程序?qū)⑼度肷a(chǎn)。J2EE 平臺(tái)的重要組件之一是 Enterprise JavaBean(EJB)API。J2EE 和 EJB 技術(shù)一起提供了許多優(yōu)點(diǎn),但隨之而來的還有一些新的挑戰(zhàn)。特別是企業(yè)系統(tǒng),其中的任何問題都必須快速得到解決。在本文中,企業(yè) Java 編程老手 Srikanth Shenoy 展現(xiàn)了他在 EJB 異常處理方面的最佳做法,這些做法可以更快解決問題。

    在 hello-world 情形中,異常處理非常簡(jiǎn)單。每當(dāng)碰到某個(gè)方法的異常時(shí),就捕獲該異常并打印堆棧跟蹤或者聲明這個(gè)方法拋出異常。不幸的是,這種辦法不足以處理現(xiàn)實(shí)中出現(xiàn)的各種類型的異常。在生產(chǎn)系統(tǒng)中,當(dāng)有異常拋出時(shí),很可能是最終用戶無法處理他或她的請(qǐng)求。當(dāng)發(fā)生這樣的異常時(shí),最終用戶通常希望能這樣:

    • 有一條清楚的消息表明已經(jīng)發(fā)生了一個(gè)錯(cuò)誤
    • 有一個(gè)唯一的錯(cuò)誤號(hào),他可以據(jù)此訪問可方便獲得的客戶支持系統(tǒng)
    • 問題快速得到解決,并且可以確信他的請(qǐng)求已經(jīng)得到處理,或者將在設(shè)定的時(shí)間段內(nèi)得到處理

    理想情況下,企業(yè)級(jí)系統(tǒng)將不僅為客戶提供這些基本的服務(wù),還將準(zhǔn)備好一些必要的后端機(jī)制。舉例來說,客戶服務(wù)小組應(yīng)該收到即時(shí)的錯(cuò)誤通知,以便在客戶打電話求助之前服務(wù)代表就能意識(shí)到問題。此外,服務(wù)代表應(yīng)該能夠交叉引用用戶的唯一錯(cuò)誤號(hào)和產(chǎn)品日志,從而快速識(shí)別問題 ― 最好是能把問題定位到確切的行號(hào)或確切的方法。為了給最終用戶和支持小組提供他們需要的工具和服務(wù),在構(gòu)建一個(gè)系統(tǒng)時(shí),您就必須對(duì)系統(tǒng)被部署后可能出問題的所有地方心中有數(shù)。

    在本文中,我們將談?wù)劵?EJB 的系統(tǒng)中的異常處理。我們將從回顧異常處理的基礎(chǔ)知識(shí)開始,包括日志實(shí)用程序的使用,然后,很快就轉(zhuǎn)入對(duì) EJB 技術(shù)如何定義和管理不同類型的異常進(jìn)行更詳細(xì)的討論。此后,我們將通過一些代碼示例來研究一些常見的異常處理解決方案的優(yōu)缺點(diǎn),我還將展示我自己在充分利用 EJB 異常處理方面的最佳做法。

    請(qǐng)注意,本文假設(shè)您熟悉 J2EE 和 EJB 技術(shù)。您應(yīng)理解實(shí)體 bean 和會(huì)話 bean 的差異。如果您對(duì) bean 管理的持久性(bean-managed persistence(BMP))和容器管理的持久性(container-managed persistence(CMP))在實(shí)體 bean 上下文中是什么意思稍有了解,也是有幫助的。請(qǐng)參閱 參考資料部分了解關(guān)于 J2EE 和 EJB 技術(shù)的更多信息。

    異常處理基礎(chǔ)知識(shí)

    解決系統(tǒng)錯(cuò)誤的第一步是建立一個(gè)與生產(chǎn)系統(tǒng)具有相同構(gòu)造的測(cè)試系統(tǒng),然后跟蹤導(dǎo)致拋出異常的所有代碼,以及代碼中的所有不同分支。在分布式應(yīng)用程序中,很可能是調(diào)試器不工作了,所以,您可能將用 System.out.println() 方法跟蹤異常。 System.out.println 盡管很方便,但開銷巨大。在磁盤 I/O 期間, System.out.println 對(duì) I/O 處理進(jìn)行同步,這極大降低了吞吐量。在缺省情況下,堆棧跟蹤被記錄到控制臺(tái)。但是,在生產(chǎn)系統(tǒng)中,瀏覽控制臺(tái)以查看異常跟蹤是行不通的。而且,不能保證堆棧跟蹤會(huì)顯示在生產(chǎn)系統(tǒng)中,因?yàn)椋?NT 上,系統(tǒng)管理員可以把 System.outSystem.err 映射到 ' ' ,在 UNIX 上,可以映射到 dev/null 。此外,如果您把 J2EE 應(yīng)用程序服務(wù)器作為 NT 服務(wù)運(yùn)行,甚至不會(huì)有控制臺(tái)。即使您把控制臺(tái)日志重定向到一個(gè)輸出文件,當(dāng)產(chǎn)品 J2EE 應(yīng)用程序服務(wù)器重新啟動(dòng)時(shí),這個(gè)文件很可能也將被重寫。

    異常處理的原則

    以下是一些普遍接受的異常處理原則:

    1. 如果無法處理某個(gè)異常,那就不要捕獲它。
    2. 如果捕獲了一個(gè)異常,請(qǐng)不要胡亂處理它。
    3. 盡量在靠近異常被拋出的地方捕獲異常。
    4. 在捕獲異常的地方將它記錄到日志中,除非您打算將它重新拋出。
    5. 按照您的異常處理必須多精細(xì)來構(gòu)造您的方法。
    6. 需要用幾種類型的異常就用幾種,尤其是對(duì)于應(yīng)用程序異常。

    第 1 點(diǎn)顯然與第 3 點(diǎn)相抵觸。實(shí)際的解決方案是以下兩者的折衷:您在距異常被拋出多近的地方將它捕獲;在完全丟失原始異常的意圖或內(nèi)容之前,您可以讓異常落在多遠(yuǎn)的地方。

    :盡管這些原則的應(yīng)用遍及所有 EJB 異常處理機(jī)制,但它們并不是特別針對(duì) EJB 異常處理的。

    由于以上這些原因,把代碼組裝成產(chǎn)品并同時(shí)包含 System.out.println 并不是一種選擇。在測(cè)試期間使用 System.out.println ,然后在形成產(chǎn)品之前除去 System.out.println 也不是上策,因?yàn)檫@樣做意味著您的產(chǎn)品代碼與測(cè)試代碼運(yùn)行得不盡相同。您需要的是一種聲明控制日志機(jī)制,以使您的測(cè)試代碼和產(chǎn)品代碼相同,并且當(dāng)記錄日志以聲明方式關(guān)閉時(shí),給產(chǎn)品帶來的性能開銷最小。

    這里的解決方案顯然是使用一個(gè)日志實(shí)用程序。采用恰當(dāng)?shù)木幋a約定,日志實(shí)用程序?qū)⒇?fù)責(zé)精確地記錄下任何類型的消息,不論是系統(tǒng)錯(cuò)誤還是一些警告。所以,我們將在進(jìn)一步講述之前談?wù)勅罩緦?shí)用程序。

    日志領(lǐng)域:鳥瞰

    每個(gè)大型應(yīng)用程序在開發(fā)、測(cè)試及產(chǎn)品周期中都使用日志實(shí)用程序。在今天的日志領(lǐng)域中,有幾個(gè)角逐者,其中有兩個(gè)廣為人知。一個(gè)是 Log4J,它是來自 Apache 的 Jakarta 的一個(gè)開放源代碼的項(xiàng)目。另一個(gè)是 J2SE 1.4 捆綁提供的,它是最近剛加入到這個(gè)行列的。我們將使用 Log4J 說明本文所討論的最佳做法;但是,這些最佳做法并不特別依賴于 Log4J。

    Log4J 有三個(gè)主要組件:layout、appender 和 category。 Layou代表消息被記錄到日志中的格式。 appender是消息將被記錄到的物理位置的別名。而 category則是有名稱的實(shí)體:您可以把它當(dāng)作是日志的句柄。layout 和 appender 在 XML 配置文件中聲明。每個(gè) category 帶有它自己的 layout 和 appender 定義。當(dāng)您獲取了一個(gè) category 并把消息記錄到它那里時(shí),消息在與該 category 相關(guān)聯(lián)的各個(gè) appender 處結(jié)束,并且所有這些消息都將以 XML 配置文件中指定的 layout 格式表示。

    Log4J 給消息指定四種優(yōu)先級(jí):它們是 ERROR、WARN、INFO 和 DEBUG。為便于本文的討論,所有異常都以具有 ERROR 優(yōu)先級(jí)記錄。當(dāng)記錄本文中的一個(gè)異常時(shí),我們將能夠找到獲取 category(使用 Category.getInstance(String name) 方法)的代碼,然后調(diào)用方法 category.error() (它與具有 ERROR 優(yōu)先級(jí)的消息相對(duì)應(yīng))。

    盡管日志實(shí)用程序能幫助我們把消息記錄到適當(dāng)?shù)某志梦恢茫鼈儾⒉荒芨龁栴}。它們不能從產(chǎn)品日志中精確找出某個(gè)客戶的問題報(bào)告;這一便利技術(shù)留給您把它構(gòu)建到您正在開發(fā)的系統(tǒng)中。

    要了解關(guān)于 Log4J 日志實(shí)用程序或 J2SE 所帶的日志實(shí)用程序的更多信息,請(qǐng)參閱 參考資料部分。

    異常的類別

    異常的分類有不同方式。這里,我們將討論從 EJB 的角度如何對(duì)異常進(jìn)行分類。EJB 規(guī)范將異常大致分成三類:

    • JVM 異常:這種類型的異常由 JVM 拋出。 OutOfMemoryError 就是 JVM 異常的一個(gè)常見示例。對(duì) JVM 異常您無能為力。它們表明一種致命的情況。唯一得體的退出辦法是停止應(yīng)用程序服務(wù)器(可能要增加硬件資源),然后重新啟動(dòng)系統(tǒng)。
    • 應(yīng)用程序異常:應(yīng)用程序異常是一種定制異常,由應(yīng)用程序或第三方的庫(kù)拋出。這些本質(zhì)上是受查異常(checked exception);它們預(yù)示了業(yè)務(wù)邏輯中的某個(gè)條件尚未滿足。在這樣的情況下,EJB 方法的調(diào)用者可以得體地處理這種局面并采用另一條備用途徑。
    • 系統(tǒng)異常:在大多數(shù)情況下,系統(tǒng)異常由 JVM 作為 RuntimeException 的子類拋出。例如, NullPointerExceptionArrayOutOfBoundsException 將因代碼中的錯(cuò)誤而被拋出。另一種類型的系統(tǒng)異常在系統(tǒng)碰到配置不當(dāng)?shù)馁Y源(例如,拼寫錯(cuò)誤的 JNDI 查找(JNDI lookup))時(shí)發(fā)生。在這種情況下,系統(tǒng)就將拋出一個(gè)受查異常。捕獲這些受查系統(tǒng)異常并將它們作為非受查異常(unchecked exception)拋出頗有意義。最重要的規(guī)則是,如果您對(duì)某個(gè)異常無能為力,那么它就是一個(gè)系統(tǒng)異常并且應(yīng)當(dāng)作為非受查異常拋出。

    受查異常是一個(gè)作為 java.lang.Exception 的子類的 Java 類。通過從 java.lang.Exception 派生子類,就強(qiáng)制您在編譯時(shí)捕獲這個(gè)異常。相反地, 非受查異常則是一個(gè)作為 java.lang.RuntimeException 的子類的 Java 類。從 java.lang.RuntimeException 派生子類確保了編譯器不會(huì)強(qiáng)制您捕獲這個(gè)異常。



    回頁(yè)首


    EJB 容器怎樣處理異常

    EJB 容器攔截 EJB 組件上的每一個(gè)方法調(diào)用。結(jié)果,方法調(diào)用中發(fā)生的每一個(gè)異常也被 EJB 容器攔截到。EJB 規(guī)范只處理兩種類型的異常:應(yīng)用程序異常和系統(tǒng)異常。

    EJB 規(guī)范把 應(yīng)用程序異常定義為在遠(yuǎn)程接口中的方法說明上聲明的任何異常(而不是 RemoteException )。應(yīng)用程序異常是業(yè)務(wù)工作流中的一種特殊情形。當(dāng)這種類型的異常被拋出時(shí),客戶機(jī)會(huì)得到一個(gè)恢復(fù)選項(xiàng),這個(gè)選項(xiàng)通常是要求以一種不同的方式處理請(qǐng)求。不過,這并不意味著任何在遠(yuǎn)程接口方法的 throws 子句中聲明的非受查異常都會(huì)被當(dāng)作應(yīng)用程序異常對(duì)待。EJB 規(guī)范明確指出,應(yīng)用程序異常不應(yīng)繼承 RuntimeException 或它的子類。

    當(dāng)發(fā)生應(yīng)用程序異常時(shí),除非被顯式要求(通過調(diào)用關(guān)聯(lián)的 EJBContext 對(duì)象的 setRollbackOnly() 方法)回滾事務(wù),否則 EJB 容器就不會(huì)這樣做。事實(shí)上,應(yīng)用程序異常被保證以它原本的狀態(tài)傳送給客戶機(jī):EJB 容器絕不會(huì)以任何方式包裝或修改異常。

    系統(tǒng)異常被定義為受查異常或非受查異常,EJB 方法不能從這種異常恢復(fù)。當(dāng) EJB 容器攔截到非受查異常時(shí),它會(huì)回滾事務(wù)并執(zhí)行任何必要的清理工作。接著,它把該非受查異常包裝到 RemoteException 中,然后拋給客戶機(jī)。這樣,EJB 容器就把所有非受查異常作為 RemoteException (或者作為其子類,例如 TransactionRolledbackException )提供給客戶機(jī)。

    對(duì)于受查異常的情況,容器并不會(huì)自動(dòng)執(zhí)行上面所描述的內(nèi)務(wù)處理。要使用 EJB 容器的內(nèi)部?jī)?nèi)務(wù)處理,您將必須把受查異常作為非受查異常拋出。每當(dāng)發(fā)生受查系統(tǒng)異常(如 NamingException )時(shí),您都應(yīng)該通過包裝原始的異常拋出 javax.ejb.EJBException 或其子類。因?yàn)?EJBException 本身是非受查異常,所以不需要在方法的 throws 子句中聲明它。EJB 容器捕獲 EJBException 或其子類,把它包裝到 RemoteException 中,然后把 RemoteException 拋給客戶機(jī)。

    雖然系統(tǒng)異常由應(yīng)用程序服務(wù)器記錄(這是 EJB 規(guī)范規(guī)定的),但記錄格式將因應(yīng)用程序服務(wù)器的不同而異。為了訪問所需的統(tǒng)計(jì)信息,企業(yè)常常需要對(duì)所生成的日志運(yùn)行 shell/Perl 腳本。為了確保記錄格式的統(tǒng)一,在您的代碼中記錄異常會(huì)更好些。

    :EJB 1.0 規(guī)范要求把受查系統(tǒng)異常作為 RemoteException 拋出。從 EJB 1.1 規(guī)范起規(guī)定 EJB 實(shí)現(xiàn)類絕不應(yīng)拋出 RemoteException



    回頁(yè)首


    常見的異常處理策略

    如果沒有異常處理策略,項(xiàng)目小組的不同開發(fā)者很可能會(huì)編寫以不同方式處理異常的代碼。由于同一個(gè)異常在系統(tǒng)的不同地方可能以不同的方式被描述和處理,所以,這至少會(huì)使產(chǎn)品支持小組感到迷惑。缺乏策略還會(huì)導(dǎo)致在整個(gè)系統(tǒng)的多個(gè)地方都有記錄。日志應(yīng)該集中起來或者分成幾個(gè)可管理的單元。理想的情況是,應(yīng)在盡可能少的地方記錄異常日志,同時(shí)不損失內(nèi)容。在這一部分及其后的幾個(gè)部分,我將展示可以在整個(gè)企業(yè)系統(tǒng)中以統(tǒng)一的方式實(shí)現(xiàn)的編碼策略。您可以從 參考資料部分下載本文開發(fā)的實(shí)用程序類。

    清單 1 顯示了來自會(huì)話 EJB 組件的一個(gè)方法。這個(gè)方法刪除某個(gè)客戶在特定日期前所下的全部訂單。首先,它獲取 OrderEJB 的 Home 接口。接著,它取回某個(gè)特定客戶的所有訂單。當(dāng)它碰到在某個(gè)特定日期之前所下的訂單時(shí),就刪除所訂購(gòu)的商品,然后刪除訂單本身。請(qǐng)注意,拋出了三個(gè)異常,顯示了三種常見的異常處理做法。(為簡(jiǎn)單起見,假設(shè)編譯器優(yōu)化未被使用。)


    清單 1. 三種常見的異常處理做法
    
    100  try {
    101    OrderHome homeObj = EJBHomeFactory.getInstance().getOrderHome();
    102    Collection orderCollection = homeObj.findByCustomerId(id);
    103    iterator orderItter = orderCollection.iterator();
    104    while (orderIter.hasNext()) {
    105      Order orderRemote = (OrderRemote) orderIter.getNext();
    106      OrderValue orderVal = orderRemote.getValue();
    107      if (orderVal.getDate() < "mm/dd/yyyy") {
    108        OrderItemHome itemHome = 
                  EJBHomeFactory.getInstance().getItemHome();
    109        Collection itemCol = itemHome.findByOrderId(orderId)
    110        Iterator itemIter = itemCol.iterator();
    111        while (itemIter.hasNext()) {
    112          OrderItem item = (OrderItem) itemIter.getNext();
    113          item.remove();
    114        }
    115        orderRemote.remove();
    116      }
    117    }
    118  } catch (NamingException ne) {
    119    throw new EJBException("Naming Exception occurred");
    120  } catch (FinderException fe) {
    121    fe.printStackTrace();
    122    throw new EJBException("Finder Exception occurred");
    123  } catch (RemoteException re) {
    124    re.printStackTrace();
    125    //Some code to log the message
    126    throw new EJBException(re);
    127  }
    

    現(xiàn)在,讓我們用上面所示的代碼來研究一下所展示的三種異常處理做法的缺點(diǎn)。

    拋出/重拋出帶有出錯(cuò)消息的異常
    NamingException 可能發(fā)生在行 101 或行 108。當(dāng)發(fā)生 NamingException 時(shí),這個(gè)方法的調(diào)用者就得到 RemoteException 并向后跟蹤該異常到行 119。調(diào)用者并不能告知 NamingException 實(shí)際是發(fā)生在行 101 還是行 108。由于異常內(nèi)容要直到被記錄了才能得到保護(hù),所以,這個(gè)問題的根源很難查出。在這種情形下,我們就說異常的內(nèi)容被“吞掉”了。正如這個(gè)示例所示,拋出或重拋出一個(gè)帶有消息的異常并不是一種好的異常處理解決辦法。

    記錄到控制臺(tái)并拋出一個(gè)異常
    FinderException 可能發(fā)生在行 102 或 109。不過,由于異常被記錄到控制臺(tái),所以僅當(dāng)控制臺(tái)可用時(shí)調(diào)用者才能向后跟蹤到行 102 或 109。這顯然不可行,所以異常只能被向后跟蹤到行 122。這里的推理同上。

    包裝原始的異常以保護(hù)其內(nèi)容
    RemoteException 可能發(fā)生在行 102、106、109、113 或 115。它在行 123 的 catch 塊被捕獲。接著,這個(gè)異常被包裝到 EJBException 中,所以,不論調(diào)用者在哪里記錄它,它都能保持完整。這種辦法比前面兩種辦法更好,同時(shí)演示了沒有日志策略的情況。如果 deleteOldOrders() 方法的調(diào)用者記錄該異常,那么將導(dǎo)致重復(fù)記錄。而且,盡管有了日志記錄,但當(dāng)客戶報(bào)告某個(gè)問題時(shí),產(chǎn)品日志或控制臺(tái)并不能被交叉引用。



    回頁(yè)首


    EJB 異常處理探試法

    EJB 組件應(yīng)拋出哪些異常?您應(yīng)將它們記錄到系統(tǒng)中的什么地方?這兩個(gè)問題盤根錯(cuò)結(jié)、相互聯(lián)系,應(yīng)該一起解決。解決辦法取決于以下因素:

    • 您的 EJB 系統(tǒng)設(shè)計(jì):在良好的 EJB 設(shè)計(jì)中,客戶機(jī)絕不調(diào)用實(shí)體 EJB 組件上的方法。多數(shù)實(shí)體 EJB 方法調(diào)用發(fā)生在會(huì)話 EJB 組件中。如果您的設(shè)計(jì)遵循這些準(zhǔn)則,則您應(yīng)該用會(huì)話 EJB 組件來記錄異常。如果客戶機(jī)直接調(diào)用了實(shí)體 EJB 方法,則您還應(yīng)該把消息記錄到實(shí)體 EJB 組件中。然而,存在一個(gè)難題:相同的實(shí)體 EJB 方法可能也會(huì)被會(huì)話 EJB 組件調(diào)用。在這種情形下,如何避免重復(fù)記錄呢?類似地,當(dāng)一個(gè)會(huì)話 EJB 組件調(diào)用其它實(shí)體 EJB 方法時(shí),您如何避免重復(fù)記錄呢?很快我們就將探討一種處理這兩種情況的通用解決方案。(請(qǐng)注意,EJB 1.1 并未從體系結(jié)構(gòu)上阻止客戶機(jī)調(diào)用實(shí)體 EJB 組件上的方法。在 EJB 2.0 中,您可以通過為實(shí)體 EJB 組件定義本地接口規(guī)定這種限制。)

    • 計(jì)劃的代碼重用范圍:這里的問題是您是打算把日志代碼添加到多個(gè)地方,還是打算重新設(shè)計(jì)、重新構(gòu)造代碼來減少日志代碼。

    • 您要為之服務(wù)的客戶機(jī)的類型:考慮您是將為 J2EE Web 層、單機(jī) Java 應(yīng)用程序、PDA 還是將為其它客戶機(jī)服務(wù)是很重要的。Web 層設(shè)計(jì)有各種形狀和大小。如果您在使用命令(Command)模式,在這個(gè)模式中,Web 層通過每次傳入一個(gè)不同的命令調(diào)用 EJB 層中的相同方法,那么,把異常記錄到命令在其中執(zhí)行的 EJB 組件中是很有用的。在多數(shù)其它的 Web 層設(shè)計(jì)中,把異常記錄到 Web 層本身要更容易,也更好,因?yàn)槟枰旬惓H罩敬a添加到更少的地方。如果您的 Web 層和 EJB 層在同一地方并且不需要支持任何其它類型的客戶機(jī),那么就應(yīng)該考慮后一種選擇。

    • 您將處理的異常的類型(應(yīng)用程序或系統(tǒng)):處理應(yīng)用程序異常與處理系統(tǒng)異常有很大不同。系統(tǒng)異常的發(fā)生不受 EJB 開發(fā)者意圖的控制。因?yàn)橄到y(tǒng)異常的含義不清楚,所以內(nèi)容應(yīng)指明異常的上下文。您已經(jīng)看到了,通過對(duì)原始異常進(jìn)行包裝使這個(gè)問題得到了最好的處理。另一方面,應(yīng)用程序異常是由 EJB 開發(fā)者顯式拋出的,通常包裝有一條消息。因?yàn)閼?yīng)用程序異常的含義清楚,所以沒有理由要保護(hù)它的上下文。這種類型的異常不必記錄到 EJB 層或客戶機(jī)層;它應(yīng)該以一種有意義的方式提供給最終用戶,帶上指向所提供的解決方案的另一條備用途徑。系統(tǒng)異常消息沒必要對(duì)最終用戶很有意義。


    回頁(yè)首


    處理應(yīng)用程序異常

    在這一部分及其后的幾個(gè)部分中,我們將更仔細(xì)地研究用 EJB 異常處理應(yīng)用程序異常和系統(tǒng)異常,以及 Web 層設(shè)計(jì)。作為這個(gè)討論的一部分,我們將探討處理從會(huì)話和實(shí)體 EJB 組件拋出的異常的不同方式。

    實(shí)體 EJB 組件中的應(yīng)用程序異常
    清單 2 顯示了實(shí)體 EJB 的一個(gè) ejbCreate() 方法。這個(gè)方法的調(diào)用者傳入一個(gè) OrderItemValue 并請(qǐng)求創(chuàng)建一個(gè) OrderItem 實(shí)體。因?yàn)?OrderItemValue 沒有名稱,所以拋出了 CreateException


    清單 2. 實(shí)體 EJB 組件中的樣本 ejbCreate() 方法
    
    public Integer ejbCreate(OrderItemValue value) throws CreateException {
        if (value.getItemName() == null) {
          throw new CreateException("Cannot create Order without a name");
        }
        ..
        ..
        return null;
    }
    

    清單 2 顯示了 CreateException 的一個(gè)很典型的用法。類似地,如果方法的輸入?yún)?shù)的值不正確,則查找程序方法將拋出 FinderException

    然而,如果您在使用容器管理的持久性(CMP),則開發(fā)者無法控制查找程序方法,從而 FinderException 永遠(yuǎn)不會(huì)被 CMP 實(shí)現(xiàn)拋出。盡管如此,在 Home 接口的查找程序方法的 throws 子句中聲明 FinderException 還是要更好一些。 RemoveException 是另一個(gè)應(yīng)用程序異常,它在實(shí)體被刪除時(shí)被拋出。

    從實(shí)體 EJB 組件拋出的應(yīng)用程序異常基本上限定為這三種類型( CreateExceptionFinderExceptionRemoveException )及它們的子類。多數(shù)應(yīng)用程序異常都來源于會(huì)話 EJB 組件,因?yàn)槟抢锸亲鞒鲋悄軟Q策的地方。實(shí)體 EJB 組件一般是啞類,它們的唯一職責(zé)就是創(chuàng)建和取回?cái)?shù)據(jù)。

    會(huì)話 EJB 組件中的應(yīng)用程序異常
    清單 3 顯示了來自會(huì)話 EJB 組件的一個(gè)方法。這個(gè)方法的調(diào)用者設(shè)法訂購(gòu) n 件某特定類型的某商品。 SessionEJB() 方法計(jì)算出倉(cāng)庫(kù)中的數(shù)量不夠,于是拋出 NotEnoughStockExceptionNotEnoughStockException 適用于特定于業(yè)務(wù)的場(chǎng)合;當(dāng)拋出了這個(gè)異常時(shí),調(diào)用者會(huì)得到采用另一個(gè)備用途徑的建議,讓他訂購(gòu)更少數(shù)量的商品。


    清單 3. 會(huì)話 EJB 組件中的樣本容器回調(diào)方法
    
    public ItemValueObject[] placeOrder(int n, ItemType itemType) throws
    NotEnoughStockException {
    
        //Check Inventory.
        Collection orders = ItemHome.findByItemType(itemType);
        if (orders.size() < n) {
          throw NotEnoughStockException("Insufficient stock for " + itemType);
        }
    }
    



    回頁(yè)首


    處理系統(tǒng)異常

    系統(tǒng)異常處理是比應(yīng)用程序異常處理更為復(fù)雜的論題。由于會(huì)話 EJB 組件和實(shí)體 EJB 組件處理系統(tǒng)異常的方式相似,所以,對(duì)于本部分的所有示例,我們都將著重于實(shí)體 EJB 組件,不過請(qǐng)記住,其中的大部分示例也適用于處理會(huì)話 EJB 組件。

    當(dāng)引用其它 EJB 遠(yuǎn)程接口時(shí),實(shí)體 EJB 組件會(huì)碰到 RemoteException ,而查找其它 EJB 組件時(shí),則會(huì)碰到 NamingException ,如果使用 bean 管理的持久性(BMP),則會(huì)碰到 SQLException 。與這些類似的受查系統(tǒng)異常應(yīng)該被捕獲并作為 EJBException 或它的一個(gè)子類拋出。原始的異常應(yīng)被包裝起來。清單 4 顯示了一種處理系統(tǒng)異常的辦法,這種辦法與處理系統(tǒng)異常的 EJB 容器的行為一致。通過包裝原始的異常并在實(shí)體 EJB 組件中將它重新拋出,您就確保了能夠在想記錄它的時(shí)候訪問該異常。


    清單 4. 處理系統(tǒng)異常的一種常見方式
    
    try {
        OrderHome orderHome = EJBHomeFactory.getInstance().getOrderHome();
        Order order = orderHome.findByPrimaryKey(Integer id);
    } catch (NamingException ne) {
        throw new EJBException(ne);
    } catch (SQLException se) {
        throw new EJBException(se);
    } catch (RemoteException re) {
        throw new EJBException(re);
    }
    

    避免重復(fù)記錄

    通常,異常記錄發(fā)生在會(huì)話 EJB 組件中。但如果直接從 EJB 層外部訪問實(shí)體 EJB 組件,又會(huì)怎么樣呢?要是這樣,您就不得不在實(shí)體 EJB 組件中記錄異常并拋出它。這里的問題是,調(diào)用者沒辦法知道異常是否已經(jīng)被記錄,因而很可能再次記錄它,從而導(dǎo)致重復(fù)記錄。更重要的是,調(diào)用者沒辦法訪問初始記錄時(shí)所生成的唯一的標(biāo)識(shí)。任何沒有交叉引用機(jī)制的記錄都是毫無用處的。

    請(qǐng)考慮這種最糟糕的情形:?jiǎn)螜C(jī) Java 應(yīng)用程序訪問了實(shí)體 EJB 組件中的一個(gè)方法 foo() 。在一個(gè)名為 bar() 的會(huì)話 EJB 方法中也訪問了同一個(gè)方法。一個(gè) Web 層客戶機(jī)調(diào)用會(huì)話 EJB 組件的方法 bar() 并也記錄了該異常。如果當(dāng)從 Web 層調(diào)用會(huì)話 EJB 方法 bar() 時(shí)在實(shí)體 EJB 方法 foo() 中發(fā)生了一個(gè)異常,則該異常將被記錄到三個(gè)地方:先是在實(shí)體 EJB 組件,然后是在會(huì)話 EJB 組件,最后是在 Web 層。而且,沒有一個(gè)堆棧跟蹤可以被交叉引用!

    幸運(yùn)的是,解決這些問題用常規(guī)辦法就可以很容易地做到。您所需要的只是一種機(jī)制,使調(diào)用者能夠:

    • 訪問唯一的標(biāo)識(shí)
    • 查明異常是否已經(jīng)被記錄了

    您可以派生 EJBException 的子類來存儲(chǔ)這樣的信息。清單 5 顯示了 LoggableEJBException 子類:


    清單 5. LoggableEJBException ― EJBException 的一個(gè)子類
    
    public class LoggableEJBException extends EJBException {
        protected boolean isLogged;
        protected String uniqueID;
    
        public LoggableEJBException(Exception exc) {
    	super(exc);
    	isLogged = false;
    	uniqueID = ExceptionIDGenerator.getExceptionID();
        }
    
    	..
    	..
    }
    

    LoggableEJBException 有一個(gè)指示符標(biāo)志( isLogged ),用于檢查異常是否已經(jīng)被記錄了。每當(dāng)捕獲一個(gè) LoggableEJBException 時(shí),看一下該異常是否已經(jīng)被記錄了( isLogged == false )。如果 isLogged 為 false,則記錄該異常并把標(biāo)志設(shè)置為 true

    ExceptionIDGenerator 類用當(dāng)前時(shí)間和機(jī)器的主機(jī)名為異常生成唯一的標(biāo)識(shí)。如果您喜歡,也可以用有想象力的算法來生成這個(gè)唯一的標(biāo)識(shí)。如果您在實(shí)體 EJB 組件中記錄了異常,則這個(gè)異常將不會(huì)在別的地方被記錄。如果您沒有記錄就在實(shí)體 EJB 組件中拋出了 LoggableEJBException ,則這個(gè)異常將被記錄到會(huì)話 EJB 組件中,但不記錄到 Web 層中。

    單 6 顯示了使用這一技術(shù)重寫后的清單 4。您還可以繼承 LoggableException 以適合于您的需要(通過給異常指定錯(cuò)誤代碼等)。


    清單 6. 使用 LoggableEJBException 的異常處理
    
    try {
        OrderHome orderHome = EJBHomeFactory.getInstance().getOrderHome();
        Order order = orderHome.findByPrimaryKey(Integer id);
    } catch (NamingException ne) {
        throw new LoggableEJBException(ne);
    } catch (SQLException se) {
        throw new LoggableEJBException(se);
    } catch (RemoteException re) {
        Throwable t = re.detail;
         if (t != null && t instanceof Exception) {
           throw new LoggableEJBException((Exception) re.detail);
         }  else {
           throw new LoggableEJBException(re);
         }
    }
    

    記錄 RemoteException

    從清單 6 中,您可以看到 naming 和 SQL 異常在被拋出前被包裝到了 LoggableEJBException 中。但 RemoteException 是以一種稍有不同 ― 而且要稍微花點(diǎn)氣力 ― 的方式處理的。
    會(huì)話 EJB 組件中的系統(tǒng)異常

    如果您決定記錄會(huì)話 EJB 異常,請(qǐng)使用 清單 7所示的記錄代碼;否則,請(qǐng)拋出異常,如 清單 6所示。您應(yīng)該注意到,會(huì)話 EJB 組件處理異常可有一種與實(shí)體 EJB 組件不同的方式:因?yàn)榇蠖鄶?shù) EJB 系統(tǒng)都只能從 Web 層訪問,而且會(huì)話 EJB 可以作為 EJB 層的虛包,所以,把會(huì)話 EJB 異常的記錄推遲到 Web 層實(shí)際上是有可能做到的。

    它之所以不同,是因?yàn)樵?RemoteException 中,實(shí)際的異常將被存儲(chǔ)到一個(gè)稱為 detail (它是 Throwable 類型的)的公共屬性中。在大多數(shù)情況下,這個(gè)公共屬性保存有一個(gè)異常。如果您調(diào)用 RemoteExceptionprintStackTrace ,則除打印 detail 的堆棧跟蹤之外,它還會(huì)打印異常本身的堆棧跟蹤。您不需要像這樣的 RemoteException 的堆棧跟蹤。

    為了把您的應(yīng)用程序代碼從錯(cuò)綜復(fù)雜的代碼(例如 RemoteException 的代碼)中分離出來,這些行被重新構(gòu)造成一個(gè)稱為 ExceptionLogUtil 的類。有了這個(gè)類,您所要做的只是每當(dāng)需要?jiǎng)?chuàng)建 LoggableEJBException 時(shí)調(diào)用 ExceptionLogUtil.createLoggableEJBException(e) 。請(qǐng)注意,在清單 6 中,實(shí)體 EJB 組件并沒有記錄異常;不過,即便您決定在實(shí)體 EJB 組件中記錄異常,這個(gè)解決方案仍然行得通。清單 7 顯示了實(shí)體 EJB 組件中的異常記錄:


    清單 7. 實(shí)體 EJB 組件中的異常記錄
    
    try {
        OrderHome orderHome = EJBHomeFactory.getInstance().getOrderHome();
        Order order = orderHome.findByPrimaryKey(Integer id);
    } catch (RemoteException re) {
        LoggableEJBException le = 
           ExceptionLogUtil.createLoggableEJBException(re);
        String traceStr = StackTraceUtil.getStackTrace(le);
        Category.getInstance(getClass().getName()).error(le.getUniqueID() +
    ":" + traceStr);
        le.setLogged(true);
        throw le;
    }
    

    您在清單 7 中看到的是一個(gè)非常簡(jiǎn)單明了的異常記錄機(jī)制。一旦捕獲受查系統(tǒng)異常就創(chuàng)建一個(gè)新的 LoggableEJBException 。接著,使用類 StackTraceUtil 獲取 LoggableEJBException 的堆棧跟蹤,把它作為一個(gè)字符串。然后,使用 Log4J category 把該字符串作為一個(gè)錯(cuò)誤加以記錄。

    StackTraceUtil 類的工作原理

    在清單 7 中,您看到了一個(gè)新的稱為 StackTraceUtil 的類。因?yàn)?Log4J 只能記錄 String 消息,所以這個(gè)類負(fù)責(zé)解決把堆棧跟蹤轉(zhuǎn)換成 String 的問題。清單 8 說明了 StackTraceUtil 類的工作原理:


    清單 8. StackTraceUtil 類
    
    
    public class StackTraceUtil {
    
    public static String getStackTrace(Exception e)
          {
              StringWriter sw = new StringWriter();
              PrintWriter pw = new PrintWriter(sw);
              return sw.toString();
          }
          ..
          ..
    }
    

    java.lang.Throwable 中缺省的 printStackTrace() 方法把出錯(cuò)消息記錄到 System.errThrowable 還有一個(gè)重載的 printStackTrace() 方法,它把出錯(cuò)消息記錄到 PrintWriterPrintStream 。上面的 StackTraceUtil 中的方法把 StringWriter 包裝到 PrintWriter 中。當(dāng) PrintWriter 包含有堆棧跟蹤時(shí),它只是調(diào)用 StringWritertoString() ,以獲取該堆棧跟蹤的 String 表示。



    回頁(yè)首


    Web 層的 EJB 異常處理

    在 Web 層設(shè)計(jì)中,把異常記錄機(jī)制放到客戶機(jī)端往往更容易也更高效。要能做到這一點(diǎn),Web 層就必須是 EJB 層的唯一客戶機(jī)。此外,Web 層必須建立在以下模式或框架之一的基礎(chǔ)上:

    • 模式:業(yè)務(wù)委派(Business Delegate)、FrontController 或攔截過濾器(Intercepting Filter)
    • 框架:Struts 或任何包含層次結(jié)構(gòu)的類似于 MVC 框架的框架

    為什么異常記錄應(yīng)該在客戶機(jī)端上發(fā)生呢?嗯,首先,控制尚未傳到應(yīng)用程序服務(wù)器之外。所謂的客戶機(jī)層在 J2EE 應(yīng)用程序服務(wù)器本身上運(yùn)行,它由 JSP 頁(yè)、servlet 或它們的助手類組成。其次,在設(shè)計(jì)良好的 Web 層中的類有一個(gè)層次結(jié)構(gòu)(例如:在業(yè)務(wù)委派(Business Delegate)類、攔截過濾器(Intercepting Filter)類、http 請(qǐng)求處理程序(http request handler)類和 JSP 基類(JSP base class)中,或者在 Struts Action 類中),或者 FrontController servlet 形式的單點(diǎn)調(diào)用。這些層次結(jié)構(gòu)的基類或者 Controller 類中的中央點(diǎn)可能包含有異常記錄代碼。對(duì)于基于會(huì)話 EJB 記錄的情況,EJB 組件中的每一個(gè)方法都必須具有記錄代碼。隨著業(yè)務(wù)邏輯的增加,會(huì)話 EJB 方法的數(shù)量也會(huì)增加,記錄代碼的數(shù)量也會(huì)增加。Web 層系統(tǒng)將需要更少的記錄代碼。如果您的 Web 層和 EJB 層在同一地方并且不需要支持任何其它類型的客戶機(jī),那么您應(yīng)該考慮這一備用方案。不管怎樣,記錄機(jī)制不會(huì)改變;您可以使用與前面的部分所描述的相同技術(shù)。



    回頁(yè)首


    真實(shí)世界的復(fù)雜性

    到現(xiàn)在為止,您已經(jīng)看到了簡(jiǎn)單情形的會(huì)話和實(shí)體 EJB 組件的異常處理技術(shù)。然而,應(yīng)用程序異常的某些組合可能會(huì)更令人費(fèi)解,并且有多種解釋。清單 9 顯示了一個(gè)示例。 OrderEJBejbCreate() 方法試圖獲取 CustomerEJB 的一個(gè)遠(yuǎn)程引用,這會(huì)導(dǎo)致 FinderExceptionOrderEJBCustomerEJB 都是實(shí)體 EJB 組件。您應(yīng)該如何解釋 ejbCreate() 中的這個(gè) FinderException 呢?是把它當(dāng)作應(yīng)用程序異常對(duì)待呢(因?yàn)?EJB 規(guī)范把它定義為標(biāo)準(zhǔn)應(yīng)用程序異常),還是當(dāng)作系統(tǒng)異常對(duì)待?


    清單 9. ejbCreate() 方法中的 FinderException
    
    public Object ejbCreate(OrderValue val) throws CreateException {
         try {
            if (value.getItemName() == null) {
              throw new CreateException("Cannot create Order without a name");
            }
            String custId = val.getCustomerId();
            Customer cust = customerHome.fingByPrimaryKey(custId);
            this.customer = cust;
         } catch (FinderException ne) {
         	  //How do you handle this Exception ?
         } catch (RemoteException re) {
    	  //This is clearly a System Exception
    	  throw ExceptionLogUtil.createLoggableEJBException(re);
         }
         return null;
    }
    

    雖然沒有什么東西阻止您把 FinderException 當(dāng)應(yīng)用程序異常對(duì)待,但把它當(dāng)系統(tǒng)異常對(duì)待會(huì)更好。原因是:EJB 客戶機(jī)傾向于把 EJB 組件當(dāng)黑箱對(duì)待。如果 createOrder() 方法的調(diào)用者獲得了一個(gè) FinderException ,這對(duì)調(diào)用者并沒有任何意義。 OrderEJB 正試圖設(shè)置客戶遠(yuǎn)程引用這件事對(duì)調(diào)用者來說是透明的。從客戶機(jī)的角度看,失敗僅僅意味著該訂單無法創(chuàng)建。

    這類情形的另一個(gè)示例是,會(huì)話 EJB 組件試圖創(chuàng)建另一個(gè)會(huì)話 EJB,因而導(dǎo)致了一個(gè) CreateException 。一種類似的情形是,實(shí)體 EJB 方法試圖創(chuàng)建一個(gè)會(huì)話 EJB 組件,因而導(dǎo)致了一個(gè) CreateException 。這兩個(gè)異常都應(yīng)該當(dāng)作系統(tǒng)異常對(duì)待。

    另一個(gè)可能碰到的挑戰(zhàn)是會(huì)話 EJB 組件在它的某個(gè)容器回調(diào)方法中獲得了一個(gè) FinderException 。您必須逐例處理這類情況。您可能要決定是把 FinderException 當(dāng)應(yīng)用程序異常還是系統(tǒng)異常對(duì)待。請(qǐng)考慮清單 1 的情況,其中調(diào)用者調(diào)用了會(huì)話 EJB 組件的 deleteOldOrder 方法。如果我們不是捕獲 FinderException ,而是將它拋出,會(huì)怎么樣呢?在這一特定情況中,把 FinderException 當(dāng)系統(tǒng)異常對(duì)待似乎是符合邏輯的。這里的理由是,會(huì)話 EJB 組件傾向于在它們的方法中做許多工作,因?yàn)樗鼈兲幚砉ぷ髁髑樾危⑶宜鼈儗?duì)調(diào)用者而言是黑箱。

    另一方面,請(qǐng)考慮會(huì)話 EJB 正在處理下訂單的情形。要下一個(gè)訂單,用戶必須有一個(gè)簡(jiǎn)檔 ― 但這個(gè)特定用戶卻還沒有。業(yè)務(wù)邏輯可能希望會(huì)話 EJB 顯式地通知用戶她的簡(jiǎn)檔丟失了。丟失的簡(jiǎn)檔很可能表現(xiàn)為會(huì)話 EJB 組件中的 javax.ejb.ObjectNotFoundExceptionFinderException 的一個(gè)子類)。在這種情況下,最好的辦法是在會(huì)話 EJB 組件中捕獲 ObjectNotFoundException 并拋出一個(gè)應(yīng)用程序異常,讓用戶知道她的簡(jiǎn)檔丟失了。

    即使是有了很好的異常處理策略,另一個(gè)問題還是經(jīng)常會(huì)在測(cè)試中出現(xiàn),而且在產(chǎn)品中也更加重要。編譯器和運(yùn)行時(shí)優(yōu)化會(huì)改變一個(gè)類的整體結(jié)構(gòu),這會(huì)限制您使用堆棧跟蹤實(shí)用程序來跟蹤異常的能力。這就是您需要代碼重構(gòu)的幫助的地方。您應(yīng)該把大的方法調(diào)用分割為更小的、更易于管理的塊。而且,只要有可能,異常類型需要多少就劃分為多少;每次您捕獲一個(gè)異常,都應(yīng)該捕獲已規(guī)定好類型的異常,而不是捕獲所有類型的異常。



    回頁(yè)首


    結(jié)束語

    我們已經(jīng)在本文討論了很多東西,您可能想知道我們已經(jīng)討論的主要設(shè)計(jì)是否都物有所值。我的經(jīng)驗(yàn)是,即便是在中小型項(xiàng)目中,在開發(fā)周期中,您的付出就已經(jīng)能看到回報(bào),更不用說測(cè)試和產(chǎn)品周期了。此外,在宕機(jī)對(duì)業(yè)務(wù)具有毀滅性影響的生產(chǎn)系統(tǒng)中,良好的異常處理體系結(jié)構(gòu)的重要性再怎么強(qiáng)調(diào)也不過分。

    我希望本文所展示的最佳做法對(duì)您有益。要深入理解這里提供的某些信息,請(qǐng)參看 參考資料部分中的清單。



    回頁(yè)首


    參考資料

    • 您可以參閱本文在 developerWorks 全球站點(diǎn)上的 英文原文.

    • 單擊本文頂部或底部的 討論參加本文的 討論論壇


    • 下載本文所討論的 實(shí)用程序類


    • 您可以閱讀 Sun Microsystems 的 EJB 規(guī)范了解關(guān)于 EJB 體系結(jié)構(gòu)的更多信息。


    • Apache 的 Jakarta 項(xiàng)目有幾個(gè)珍品。 Log4J 框架即是其中之一。


    • Struts 框架是 Jakarta 項(xiàng)目的另一個(gè)珍品。Struts 建立在 MVC 體系結(jié)構(gòu)的基礎(chǔ)上,提供了一個(gè)徹底的分離,它把系統(tǒng)的表示層從系統(tǒng)的業(yè)務(wù)邏輯層中分離出來。


    • 要詳細(xì)了解 Struts,請(qǐng)閱讀 Malcom Davis 所寫的講述這個(gè)主題的很受歡迎的文章“ Struts, an open-source MVC implementation”( developerWorks,2001 年 2 月)。請(qǐng)注意:有一篇由 Wellie Chao 撰寫的最新文章定于 2002 年夏季發(fā)表。


    • 您可以通過閱讀相關(guān)的 J2SE 文檔了解關(guān)于新的 Java Logging API(java.util.logging)的更多信息。


    • 剛接觸 J2EE?來自“WebSphere 開發(fā)者園地”的這篇文章告訴您如何 用 WebSphere Studio Application Developer 開發(fā)和測(cè)試 J2EE 應(yīng)用程序(2001 年 10 月)。


    • 如果您想更多了解關(guān)于測(cè)試基于 EJB 的系統(tǒng)的知識(shí),請(qǐng)從最近的 developerWorks文章“ Test flexibly with AspectJ and mock objects”(2002 年 5 月)開始。


    • 如果您不滿足于單元測(cè)試,還想了解企業(yè)級(jí)系統(tǒng)測(cè)試的知識(shí),請(qǐng)看看 IBM Performance Management, Testing, and Scalability Services企業(yè)級(jí)測(cè)試庫(kù)提供了什么。


    • Sun 的 J2EE 模式Web 站點(diǎn)著重于使用 J2EE 技術(shù)的模式、最佳做法、設(shè)計(jì)策略以及經(jīng)驗(yàn)證的解決方案。


    • 您可以在 developerWorks Java 技術(shù)專區(qū)找到數(shù)以百計(jì)關(guān)于 Java 編程的方方面面的文章。


    回頁(yè)首


    關(guān)于作者

    Srikanth Shenoy 的照片

    Srikanth Shenoy 專門從事大型 J2EE 和 EAI 項(xiàng)目的體系結(jié)構(gòu)、設(shè)計(jì)、開發(fā)和部署工作。他在 Java 平臺(tái)一出現(xiàn)時(shí)就迷上了它,從此便全心投入。Srikanth 已經(jīng)幫他的制造業(yè)、物流業(yè)和金融業(yè)客戶實(shí)現(xiàn)了 Java 平臺(tái)“一次編寫,隨處運(yùn)行”的夢(mèng)想。您可以通過 srikanth@srikanth.org與他聯(lián)系。

    posted @ 2005-12-10 11:25 GHawk 閱讀(338) | 評(píng)論 (0)編輯 收藏

    主站蜘蛛池模板: 亚洲精品国产精品乱码在线观看| 国产精品亚洲专区在线播放| 免费国产在线视频| 亚洲熟女乱综合一区二区| 亚洲熟妇成人精品一区| 97视频免费在线| 97亚洲熟妇自偷自拍另类图片| a毛片免费在线观看| 亚洲精品亚洲人成在线观看下载| 亚洲AV成人无码网天堂| 国产精品视频免费一区二区| 青青草原精品国产亚洲av| 久久精品成人免费看| 亚洲愉拍99热成人精品热久久| 免费一级毛片在线播放放视频| 永久免费毛片手机版在线看| 亚洲综合成人婷婷五月网址| 无遮免费网站在线入口| 亚洲二区在线视频| 亚洲一区免费在线观看| 亚洲精品在线播放| 久久精品一本到99热免费| 久久亚洲私人国产精品vA| 亚洲精品免费在线观看| 久久亚洲中文字幕精品有坂深雪| 三年片在线观看免费大全电影 | 亚洲国产成+人+综合| 永久免费在线观看视频| 亚洲成人动漫在线观看| 国产曰批免费视频播放免费s| 亚洲激情黄色小说| 男女免费观看在线爽爽爽视频 | 无码人妻久久一区二区三区免费| 亚洲乱码日产一区三区| 久久精品免费一区二区三区| 亚洲av无码专区在线播放| 99爱在线精品视频免费观看9| 91亚洲国产成人久久精品网站| h视频在线免费看| 亚洲一区精彩视频| 日韩精品免费电影|