|
Unwrap the package statement's potential (挖掘package聲明的潛力) Minimize project complexity and maximize code reuse (最小化項(xiàng)目的復(fù)雜性,最大化代碼的復(fù)用程度) by Laurence Vanhelsuwe
總評(píng) package聲明是Java語言的一項(xiàng)非常強(qiáng)有力的特征。然而大多數(shù)的Java程序員,甚至那些富有經(jīng)驗(yàn)的開發(fā)者,并沒有正確的挖掘出它的潛力來。更糟的是,許多開發(fā)者錯(cuò)誤的使用了package的聲明,使得它在試圖控制項(xiàng)目復(fù)雜性和代碼的復(fù)用方面出現(xiàn)問題。感興趣了嗎?那就繼續(xù)閱讀下去,看看這樣一個(gè)簡(jiǎn)單的語言特征是如何擁有如此巨大的反響的。
為了避免最高級(jí)別的package命名沖突,除了眾所周知的并且通常由Sun微系統(tǒng)公司申明的package命名約定之外,很少有程序員徹底地理解這個(gè)既讓人迷惑但又是簡(jiǎn)單的package聲明了。大多數(shù)的編程者認(rèn)為關(guān)鍵字package最多就是用來將項(xiàng)目中的class整理成組。他們簡(jiǎn)單的使用package聲明來為每個(gè)項(xiàng)目創(chuàng)建單一的命名空間。但不幸的是,這種方式并不能經(jīng)受時(shí)間或者范圍的考驗(yàn)。
當(dāng)一種過分簡(jiǎn)單化的package觀念添加到以團(tuán)隊(duì)為范圍(先不說企業(yè)為范圍)的Java代碼倉(cāng)庫中時(shí),您就會(huì)逐漸地同時(shí)也是痛苦地發(fā)現(xiàn),錯(cuò)誤地創(chuàng)造和管理Java代碼倉(cāng)庫中的package層級(jí)意味著十分的昂貴和費(fèi)力。更糟的是,這些問題將隨著您代碼的逐漸龐大而變得越來越嚴(yán)重,特別地,還會(huì)給代碼帶來項(xiàng)目邊界模糊的問題。
因此,從使用package聲明的那一刻起,就必須正確的做一些選擇和決議。
在這篇文章中,我將解釋為什么許多的Java程序員沒有正確的使用package關(guān)鍵字,然后演示一個(gè)可供選擇的并且已經(jīng)經(jīng)受了時(shí)間考驗(yàn)的一些方式。
新手使用Java package的方式 在你最初使用Java來編寫程序的時(shí)候,一般是根本不會(huì)使用包聲明的。那個(gè)用來作為語言介紹的經(jīng)典HelloWorld程序確實(shí)沒有使用package,也沒有以任何方式來討論Java package或者package關(guān)鍵字:
// (沒有使用package聲明!)
class HelloWorld { public static void main(String[] args) { System.out.println("Hello World"); } }
你只是簡(jiǎn)單地聲明你的類在缺省的package下(沒有名字的package),所以你可以以最詳細(xì)(verbose)的方式來運(yùn)行你的Java代碼(比如在你控制臺(tái)的命令行):
> java HelloWorld
幸運(yùn)的是,Java的設(shè)計(jì)并沒有因這種代碼執(zhí)行的便利性而有任何的削弱。然而優(yōu)雅并且強(qiáng)大地支持大規(guī)模的項(xiàng)目編碼是程序設(shè)計(jì)的最高目標(biāo),這正是通過package這個(gè)特征來顯現(xiàn)出來的。因此,以缺省package的方式來作為類聲明在你真正實(shí)現(xiàn)項(xiàng)目的時(shí)候是并不足以支撐下去的。
類名沖突和package的誕生 隨著您對(duì)Java越來越適應(yīng),您將會(huì)很快發(fā)現(xiàn)把所有的類都放置在缺省包下,就將受限于這個(gè)缺省包空間所能容納的實(shí)際的類的數(shù)量。比如,假設(shè)您起初的一些用于練習(xí)的類起名為Main或者Program,而您的實(shí)際的項(xiàng)目同樣需要一個(gè)名為Main或者Program的類來作為程序主入口,那么您就會(huì)發(fā)現(xiàn)類名起了沖突。要么刪除些舊的類,要么通過把全局空間細(xì)分成多個(gè)命名空間來創(chuàng)建多個(gè)包空間以解決問題。
Java新手們特別的也是最終發(fā)現(xiàn)的關(guān)于包聲明的問題在于當(dāng)他們開始第二個(gè)Java項(xiàng)目,并且希望對(duì)他們第一個(gè)項(xiàng)目中的類和第二個(gè)項(xiàng)目中的類有一個(gè)清晰分離的時(shí)候。
很快的,為每個(gè)新項(xiàng)目創(chuàng)建新的包變成了程序員的第二個(gè)習(xí)性。不幸的是,許多的Java程序員對(duì)于真正的package的理解僅限于這一點(diǎn)上。但是以這種原始的方式在那些跨度比較長(zhǎng)的項(xiàng)目中繼續(xù)使用Java包特征無疑是遠(yuǎn)不能滿足要求的,尤其是當(dāng)代碼量由小逐漸變得很大的時(shí)候。
代碼復(fù)制:堅(jiān)決說不 簡(jiǎn)單的為每個(gè)新項(xiàng)目創(chuàng)建新的包的一個(gè)長(zhǎng)期的問題是代碼的復(fù)制問題。代碼復(fù)制是編寫程序的地獄,這是因?yàn)椋?br> # 維護(hù)的代價(jià)將由于螺旋狀的上升而變得難以控制 # 代碼難以閱讀 # 代碼變得越來越臃腫 # 系統(tǒng)性能可能會(huì)變得緩慢下來
我們都知道這些問題的根源:程序員那標(biāo)志性的懶惰。這個(gè)過程通常是這樣的:在我們工作的時(shí)候,我們會(huì)以自己的感覺認(rèn)為這些是以前就已經(jīng)做好的或者已經(jīng)解決的(代碼中邏輯的部分,或者整個(gè)方法,或者(希望不是這樣!)整個(gè)類),于是我們高興的把那些代碼復(fù)制到新項(xiàng)目中來。這也就是剪切加復(fù)制編碼的來源。
如果您對(duì)于自己的那些用近乎于一致的邏輯、方法甚至類所堆砌成的重復(fù)代碼產(chǎn)生了難以控制的感覺,那么您就有必要對(duì)您每天使用的Java開發(fā)方法論進(jìn)行反思,以釋放包申明真正的力量。
The Big Bang...uh, I mean split [不能確切的翻譯,故保留] 讓我們從理論上來分析一下代碼復(fù)制的問題,認(rèn)定所有的代碼復(fù)制是非法的并且根除它,任何重要的代碼片斷都應(yīng)該出現(xiàn)并且只出現(xiàn)一次。這就是說,所有任何的
# 通用的邏輯 # 通用的數(shù)據(jù)分組 # 通用的方法/程序 # 通用的常量 # 通用的類 # 通用的接口
都不應(yīng)當(dāng)被申明在應(yīng)用特定的包中。
這一關(guān)鍵的結(jié)論讓我們有了如下的有關(guān)包結(jié)構(gòu)的第一個(gè)黃金規(guī)則:
黃金規(guī)則一 永遠(yuǎn)不要將通用的代碼直接混合在應(yīng)用代碼中!
比如:在com.company或者org.yourorg這一層上,將你的包層次再分為兩個(gè)功能完全不同的分支:
1. 可復(fù)用的代碼分支 2. 項(xiàng)目(應(yīng)用)特定的分支
應(yīng)用代碼總是會(huì)用到通用的代碼(類庫以及程序),但是它們自己并不會(huì)包含這樣的代碼。相反的情況是:類庫代碼也永遠(yuǎn)不會(huì)包含任何應(yīng)用特定的代碼或者是和應(yīng)用有依賴關(guān)系的代碼。
如果你還從來沒有考慮(編寫)過這樣兩種不同類型的基本代碼的話,那么你有必要在你日常的編碼過程中考慮一下這種基本的代碼歸類(dichotomy)方法。這是在你的組織中應(yīng)用代碼復(fù)用能力,以及一次性的消除代碼復(fù)制問題的關(guān)鍵所在。
這種代碼的歸類方法運(yùn)用到package上的時(shí)候,邏輯上需要從最高級(jí)別上分為通用(復(fù)用)package的主分支以及非通用(非復(fù)用)(比如:應(yīng)用特定的)主分支。
舉個(gè)例子,在過去的五年中,我已經(jīng)把org.lv這個(gè)最高級(jí)的命名空間分為了org.lv.lego和org.lv.apps這兩個(gè)子空間。(lv并不代表什么,只是我最初的命名)這些基本的高級(jí)別的分支在以后的日子里面又分成了更多的子空間。比如lego分支目前分成如下的一些子空間:
org.lv.lego.adt org.lv.lego.animation org.lv.lego.applets org.lv.lego.beans org.lv.lego.comms org.lv.lego.crunch org.lv.lego.database org.lv.lego.files org.lv.lego.games org.lv.lego.graphics org.lv.lego.gui org.lv.lego.html org.lv.lego.image org.lv.lego.java org.lv.lego.jgl org.lv.lego.math org.lv.lego.realtime org.lv.lego.science org.lv.lego.streams org.lv.lego.text org.lv.lego.threads
請(qǐng)注意的是,基本上所有這些包結(jié)構(gòu)的邏輯內(nèi)容都自我證明了它們是經(jīng)過仔細(xì)選擇后的結(jié)果,并且也是從字面上就可以分辨出它們的含義(可以和java.*結(jié)構(gòu)做對(duì)照)。這一點(diǎn)對(duì)于釋放可復(fù)用代碼真正的潛力作用是非常關(guān)鍵的,比如那些可復(fù)用的邏輯、程序、常量、類以及接口。很差的包命名,就象很差的類/接口的命名一樣,會(huì)對(duì)它們的使用者產(chǎn)生歧義并且破壞資源的復(fù)用潛力。
在這些較深層次的包級(jí)別上,您仍然需要對(duì)如何進(jìn)一步組織您的包結(jié)構(gòu)非常的小心。
這里是第二條黃金規(guī)則:
黃金規(guī)則二 保持分等級(jí)的包結(jié)構(gòu) 總是試圖創(chuàng)建象平衡的、不規(guī)則形狀的樹結(jié)構(gòu)那樣的包層次。
如果你的包層次中的某些層次最終形成(退化為)線性結(jié)構(gòu),那表明了你并沒有正確的使用Java包特性。經(jīng)典的錯(cuò)誤是簡(jiǎn)單的將項(xiàng)目包羅列在你的最高層應(yīng)用包分支下面,比如在我的org.lv.apps包。這是錯(cuò)誤的使用方式,因?yàn)樗⒉皇菍蛹?jí)結(jié)構(gòu)的。線性羅列對(duì)于人腦來說是難以記憶很長(zhǎng)時(shí)間的;而層級(jí)結(jié)構(gòu)卻正是適應(yīng)我們大腦的神經(jīng)系統(tǒng)網(wǎng)絡(luò)結(jié)構(gòu)的。
項(xiàng)目總是能由關(guān)鍵原則來進(jìn)行歸類,這原則或者說觀念應(yīng)該在你的Java包層次中反應(yīng)出來。下面是我的org.lv.apps包目前是如何進(jìn)行細(xì)分的:
org.lv.apps.comms org.lv.apps.dirs org.lv.apps.files org.lv.apps.games org.lv.apps.image org.lv.apps.java org.lv.apps.math
很明顯,你們的細(xì)分肯定和我的不相同,但是重要的是從大處考慮并且在腦子里始終保留對(duì)將來的擴(kuò)展能力。深層次的包結(jié)構(gòu)是有益的。而淺層次的則不然。
|
溫馨提示:喜歡本站的話,請(qǐng)收藏一下本站!