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