看了neora的大作
寫給我的團(tuán)隊
,頗受啟發(fā),在這里我借花獻(xiàn)佛,也寫一些短文給團(tuán)隊的新老成員做些總結(jié)。照搬的地方neora老大表罵我
?
<!----><!----> 各位尊敬的同事
你們好!我知道大家都很忙,忙的連寫注釋和文檔的時間都沒有,更不要說做總結(jié)了。所以我就寫一些短文,幫助大家總結(jié)一下。正如大家所知道的,我們的團(tuán)隊每天所面對的問題有很多——需求、測試、編碼、變更、架構(gòu) …… ,好吧,就讓我們從編碼開始吧。
為什么要編碼?
軟件就是把人們的需要轉(zhuǎn)化為計算機(jī)可以執(zhí)行的程序。
這一點(diǎn)毫無疑問,但是這就是我們編碼的目的嗎?我們都知道,計算機(jī)其實(shí)很笨,它只能認(rèn)識 0 和 1 這兩個數(shù)字,無論多么復(fù)雜的程序都是由這兩個最簡單的數(shù)字構(gòu)成。這正如哥德巴赫猜想,作為最復(fù)雜的數(shù)學(xué)證明題,要求解決的卻是最簡單的 1+1 問題。也就是說,最直接的編程就是向計算機(jī)輸入 0 或者 1 ,為什么我們不這樣呢?
原因當(dāng)然很簡單,因?yàn)? 0 和 1 組成的機(jī)器語言實(shí)在是太難以理解和記憶了。于是前輩們就發(fā)明了匯編語言,匯編語言用單詞 代替了機(jī)器語言,它能夠讓人們更加容易理解代碼和程序。可是前輩們又發(fā)現(xiàn)匯編語言還是太晦澀難懂,幾百行代碼已經(jīng)讓人云山霧罩,遇到幾萬行的大型程序,那 簡直就是天書了。所以先行者們又發(fā)明了高級語言,高級語言用接近自然語言的方式來編寫程序,就象自然語言一樣,它(們)成為了程序員闡明觀點(diǎn)交流思想的通 用工具。
所以,我們編寫代碼的目的就是為了交流和溝通,而這,也正是計算機(jī)語言存在的意義。
考慮別人
既 然編寫代碼的目的是為了溝通,那么作為一個程序員有什么理由去寫那些只有自己才能夠看懂的代碼呢。如果吧這個作為一個問題問大家,恐怕沒有一個人會這樣回 答:“編寫只有自己能看懂的代碼,讓別人去猜吧!”。因?yàn)榇蠹叶枷M献鳎荚敢馀c其他人溝通,這不僅僅是工作的需要也是人類的基本需要之一。
可是,并不是每一個程序員都懂得溝通的藝術(shù)。幾年前,我認(rèn)識了一個很有天分的程序員,他的編程能力讓人吃驚。但是,當(dāng)他把一個命名為“ tj ”的函數(shù)交給我的時候,我只有無語了——“統(tǒng)計?”“添加?”,反復(fù)猜測不得要領(lǐng)之后,我只好讓他重寫這個函數(shù)。這樣的例子還有很多,幾乎每一個新手都出現(xiàn)過這樣問題,即使他們對所使用的編程語言掌握的無比純熟。
那么怎樣才能編寫一份能夠閱讀并易于理解的代碼呢?下面我給出一些建議,其中的一些已經(jīng)作為我們團(tuán)隊《編程規(guī)范》的一部分而被大家了解了,而另一些則不然。
<!----><!----><!----> ● ???? <!----> 時刻考慮你所編寫的每一行代碼、代碼中的每一個單詞都會在將來某個時候被你的伙伴們閱讀、測試和維護(hù)。這是一個原則,也是一個前提,是每一個程序員所必須具備的基本素質(zhì)。在一個團(tuán)隊中,編寫可以閱讀和維護(hù)的代碼是團(tuán)隊合作的前提條件。
<!----><!----><!----> <!----> ●? ? <!----> 必須認(rèn)真的慎重的為你的變量、函數(shù)(方法)、類命名。要起一個科學(xué)合理的名字,因?yàn)槊质抢斫獯a的重要依據(jù)。同時,一旦代碼發(fā)布,其依賴者、繼承者都要永遠(yuǎn)維持這種命名。想像一下,如果你和你的同事不得不使用類似“ tj ”這樣的名字,而又不能改變現(xiàn)狀(會影響其他使用者),那該是一件多么痛苦的事情。
<!----><!----><!----> ● 為你的代碼編寫注釋。注釋是代碼的重要組成部分,同時也是別人理解你代碼的重要依據(jù),無論任務(wù)多么繁重,時間多么緊張,都應(yīng)該象對待代碼一樣對待你的注釋。哦,還有,最重要的是維護(hù)你的代碼的同時要維護(hù)注釋,就像下面一條所說的:
<!----><!----><!----> ●? 請確保注釋是有效的。無效的,甚至是錯誤的注釋還不如不寫注釋,這是顯而易見的。注釋應(yīng)該言簡意賅,不要用注釋重復(fù)你的代碼,也不要用注釋闡述代碼中顯而易見的邏輯。請原諒我的啰嗦——在維護(hù)代碼的同時不要忘記維護(hù)注釋。
<!----><!----><!----> <!----> ● ? <!----> 空行、空格也是代碼。空行是一個邏輯段起止的標(biāo)志,它和編程者的思路是一致的。另外,適當(dāng)?shù)氖褂每招泻涂崭窨梢允鼓愕拇a更加清晰。
<!----><!----><!----> <!----> ● ? <!----> 不要耍小聰明。只要有可能,請盡量使用平實(shí)的 思路來規(guī)劃你的代碼。我們都知道,閱讀別人的代碼往往比自己編寫同樣功能的代碼要困難,因?yàn)殚喿x代碼的過程就是讀懂對方思想的過程。困難的原因之一是作者 的水平比讀者要高——這當(dāng)然是讀者所歡迎的;但是,也可能是因?yàn)樽髡呤褂昧嘶逎y懂的思路和技巧,這些技巧五花八門無法列舉,下面是常見的幾種情況:
<!---->
1)
???????
<!---->
過度的使用了設(shè)計模式。
這是設(shè)計模式初學(xué)者經(jīng)常犯的錯誤,設(shè)計模式是面向?qū)ο笏枷氲木A,但是對于經(jīng)驗(yàn)較少的程序員來說,設(shè)計模式是抽象的和復(fù)雜的,過多的使用設(shè)計模式,會使得代碼結(jié)構(gòu)復(fù)雜難以理解。
<!---->
2)
???????
<!---->
拼湊結(jié)果。
例如,某個復(fù)雜的函數(shù)返回的值總是比預(yù)期結(jié)果少
1
,有些程序員會采用
+1
的方式使結(jié)果正確。這類做法往往使得代碼邏輯混亂莫名其妙,更為嚴(yán)重的是,類似的方法很冗余導(dǎo)致程序中隱藏著錯誤。
<!---->
3)
???????
<!---->
過度封裝。
經(jīng)常遇到的一種情況就是對第三方類庫的進(jìn)一步封裝,這必須在有經(jīng)驗(yàn)的設(shè)計者的指導(dǎo)下進(jìn)行,因?yàn)轭愃频男枨笸霈F(xiàn)在各種架構(gòu)級別的模塊中。對第三方類庫的不合理封裝會導(dǎo)致使用著對類庫的使用產(chǎn)生異議,出現(xiàn)錯誤的用法等。
<!---->
4)
???????
<!---->
拒絕使用成熟的第三方類庫。
許多初級設(shè)計人員,往往喜歡自己編寫框架級別的代碼,例如,在
JavaEE
開發(fā)中自行設(shè)計
MVC
框架。這類做法在多數(shù)情況下都是錯誤的,因?yàn)槌墒斓漠a(chǎn)品比比皆是,這些產(chǎn)品都有廣泛的用戶群、詳盡的文檔、活躍的社區(qū)和大量的成熟案例。普通開發(fā)人員極少能夠自行編寫同樣水準(zhǔn)的代碼,而且,即便寫出來了誰又能夠保證其他開發(fā)人員會使用并且喜歡使用呢。
<!---->
5)
???????
<!---->
冒然采用新的技術(shù)。
程序員是思維活躍敢于嘗試創(chuàng)新的人群,他們往往樂于使用新的技術(shù)和新的產(chǎn)品。但是,在使用這些產(chǎn)品之前請仔細(xì)考慮:“你了解它嗎
?
”。冒然在團(tuán)隊中應(yīng)用新技術(shù)是一種非常不負(fù)責(zé)任的行為,學(xué)習(xí)曲線、隱藏
BUG
、適用范圍等等會成為應(yīng)用新技術(shù)的障礙,嚴(yán)重的情況下一個類似的決策會導(dǎo)致整個項目的失敗。
?
消除重復(fù)
如果說軟件目的就是將需求轉(zhuǎn)換為程序,那么軟件的本質(zhì)就是復(fù)用。復(fù)用是一個非常寬泛概念,微軟將 Windows 操作系統(tǒng)刻錄成光盤出售,用戶買到的都是同樣的程序,這就是復(fù)用;我們使用 163 或者 google 的 Email 服務(wù),訪問同樣的 Web 網(wǎng)站,這,也是復(fù)用。一個程序、一個 URL 、一個架構(gòu)、一個類、一個方法都可以成為復(fù)用的對象。這里,我們只討論代碼的復(fù)用。
<!----><!----><!---->
●
僅在需要的時候使用接口。
接口為我們的應(yīng)用提供很好的“可替換性”,簡單的說,使用者僅依賴接口,而不必考慮接口 是如何實(shí)現(xiàn)的,這樣,一旦發(fā)生變化只需要重新實(shí)現(xiàn)接口,而依賴于接口的代碼不必改動。顯然,接口是非常強(qiáng)大的,它使得我們的代碼具有擴(kuò)展性,但是,是否所 有的模塊都需要這種擴(kuò)展性呢?實(shí)踐證明,
80%
左右的模塊不需要替換實(shí)現(xiàn)類,這些模塊即使發(fā)生了變化也往往是局部的,不會影響其他模塊。而對它們的修改和維護(hù)的工作量要遠(yuǎn)遠(yuǎn)小于重新實(shí)現(xiàn)它們的接口。這類模塊,是不需要接口的。那么什么時候使用接口呢,下面列出幾種常見的情況:
<!---->
1)
?
<!---->
兩個并行開發(fā)的模塊(或類、子系統(tǒng)),互相存在依賴關(guān)系。
例如,
A
模塊依賴
B
模塊的某些功能,因?yàn)槭遣⑿虚_發(fā)的,所以
B
模塊可能還沒有完成,這個時候,需要
B
模塊提供接口以供
A
模塊使用。
A
模塊開發(fā)的時候,可以先使用一個
B
模塊接口的“模擬實(shí)現(xiàn)”,等到
B
模塊完成,再替換為真實(shí)的實(shí)現(xiàn)。
<!---->
2)
?
<!---->
對于結(jié)構(gòu)復(fù)雜的或提供公共服務(wù)的模塊。
有時候,需要提供一些公共的服務(wù)為整個系統(tǒng)使用。例如,在
Java
中直接使用
JDBC
操作數(shù)據(jù)庫往往比較繁瑣,這個使用需要提供一個通用的
JDBC
封裝,將重復(fù)繁瑣的
JDBC
操作提取到抽象類或者工具類中。但是,
JDBC
操作中并非所有的代碼都是重復(fù)的和可預(yù)見的,此時應(yīng)該將這些非重復(fù)的和不可預(yù)見的代碼抽象為接口,工具類依賴接口編程,工具類的使用者來根據(jù)自身情況實(shí)現(xiàn)這些接口。這其實(shí)是一個策略模式的應(yīng)用,在實(shí)際開發(fā)中是很常見的。
<!---->
3)
?
<!---->
當(dāng)模塊的可變性是可以預(yù)見的時候。
如果開發(fā)人員預(yù)見到:“這個模塊將來一定會發(fā)生某種變化”,那么此時可以使用接口。這種 情況下,接口的粒度一定要細(xì),僅僅包含變化的功能即可。例如,一組結(jié)構(gòu)相同的數(shù)據(jù),需要我們對其中一些特殊的數(shù)據(jù)進(jìn)行處理,而原始數(shù)據(jù)中并沒有提供某種規(guī) 律來辨別哪些數(shù)據(jù)是特殊的,此時,我們只好通過硬編碼(
Hard Code
)來處理這些特殊數(shù)據(jù)(例如,通過名稱來判斷)。這顯然是代碼中的壞味道,解決的辦法就是提供一個用于判別特殊性的接口,然后用
Hard Code
的部分來實(shí)現(xiàn)接口,當(dāng)客戶方提供了數(shù)據(jù)的規(guī)律的時候,重新實(shí)現(xiàn)一個就可以了。
<!----> 4) ? <!----> 在某些大型項目中,不同的模塊可能需要不同的團(tuán)隊甚至不同公司進(jìn)行開發(fā)。這個時候需要使用接口來規(guī)范雙方之間的通信。
?
<!----><!----><!----> <!---->
●
用抽象類實(shí)現(xiàn)公用的代碼。
面向?qū)ο蟮脑瓌t告訴我們,聚合復(fù)用應(yīng)該優(yōu)先于繼承復(fù)用。但是,當(dāng)你確定某些類
從本質(zhì)上講是同一個類別
的時候,就應(yīng)該考慮將這些類的公共部分提取到抽象類中,以實(shí)現(xiàn)代碼的復(fù)用。經(jīng)過這種處理的子類比原來要精簡很多,甚至比使用了聚合復(fù)用還要精簡。
<!----><!----><!----> <!----> ● 在復(fù)雜的模塊中使用設(shè)計模式。
<!----><!----><!----> <!---->
●
適量的使用條件分支判斷。
大量的條件分支判斷會導(dǎo)致代碼冗長、結(jié)構(gòu)松散、邏輯混亂,這類代碼往往沒有可閱讀性和可維護(hù)性。但是,并非
if-else
就不能使用的了,只要遵循下面的原則,
if-else
仍然可以使用:
<!----> 1) ????? <!----> 一組條件分支只判斷一類條件。多種條件混合在一組分支中只能說明編程者的思維混亂。
<!----> 2) ????? <!----> 分支要盡可能少,不要超過 8 個。
<!----> 3) ????? <!----> 每個分支的代碼在 3 行以內(nèi)。
<!----> 4) ????? <!----> 每個分支都有注釋。
<!----> 5) ????? <!----> 最后一個分支用于判斷缺省情況。
<!----> 6) ????? <!----> 如果上述 5 條不能同時滿足,請休息片刻,然后重構(gòu)你的代碼。
<!----><!----><!---->
●
不要
Copy-Paste
你的代碼。
當(dāng)你
Copy-Paste
代碼的時候,說明代碼中存在重復(fù),重復(fù)的代碼往往導(dǎo)致代碼難以維護(hù)和閱讀。一旦那些保存在剪切板中的代碼中存在錯誤,編寫者甚至不知道到哪里修改這些錯誤。每當(dāng)你
Copy-Paste
代碼的時候,請停下來,考慮將這些代碼提取為方法、類或者組件。
源代碼就是文檔
我們?yōu)槭裁磳懺O(shè)計文檔呢?設(shè)計文檔可以說明你的代碼,闡明設(shè)計思路,文檔是我們溝通的重要工具,它可以使軟件具有可持續(xù)發(fā)展性。這里我只想說說詳細(xì)設(shè)計文檔。 幾年 前,我一直有一些疑問,什么是概要設(shè)計和詳細(xì)設(shè)計?怎樣才算“概要”?怎樣才算“詳細(xì)”?詳細(xì)到什么程度才算合格?
現(xiàn)在我已經(jīng)明白了,所謂概要設(shè)計就是架構(gòu)設(shè)計,架構(gòu)就是將一個軟件中功能性的東西剔除之后剩下的部分。這一部分是軟件的結(jié)構(gòu),它說明了軟件中最為重要的特性和這些特性的實(shí)現(xiàn)方式。
那么詳細(xì)設(shè)計呢?我曾經(jīng)看到過的詳細(xì)設(shè)計可以細(xì)致到偽代碼,程序員可以不必動腦筋,直接將偽代碼翻譯為 Java 或者 C# 代碼即可。我對這樣的設(shè)計人員致以無比的敬意,因?yàn)樗▊儯┎坏梢詾閿?shù)量遠(yuǎn)超他們的程序員寫代碼,更為神奇的是,這些代碼可以在沒有調(diào)試過的情況下正常運(yùn)行。但是,我想今生我都無法達(dá)到這個水平了。而且即使我達(dá)到了這個水平,我還要為這些設(shè)計文檔的維護(hù) 尤其是文檔與代碼之間的同步 付出無比艱辛的勞動,與其這樣,我不如:
<!----><!----><!---->?<!----><!---->
<!---->
●
???????
<!---->
簡單設(shè)計。
Kent Beck
在《解析極限編程——擁抱變化》中為簡單設(shè)計制定了
4
個評價標(biāo)準(zhǔn),依次為(最重要的排在最前面):通過所有測試;體現(xiàn)所有意圖;避免重復(fù);類 或者方法數(shù)量最少。這其中包含的核心意義就是不要為了考慮程序的可擴(kuò)展性,把目前不需要的功能加入到軟件中來。不要為了遵循某種規(guī)范而編寫大量無用的甚至 是起到反作用的文檔,將精力轉(zhuǎn)移到代碼上來。依附在良好架構(gòu)上的優(yōu)質(zhì)代碼,比任何文檔的作用都明顯。
<!---->
●
???????
<!---->
單元測試的文檔作用。
測試方面我將另行撰文闡述,這里所說的是單元測試的文檔作用。任何一個學(xué)習(xí)計算機(jī)編程的人都知道,
10
行文字說明不如一行代碼演示,這體現(xiàn)了“例子”的重要性。單元測試對于維護(hù)者而言就是“例子”,當(dāng)維護(hù)者難以理解你的代碼,當(dāng)使用者不知如何使用你寫的
API
,當(dāng)你自己不知道修改代碼會造成哪些影響的時候,單元測試可以為你說明一切。所以,請每 一個程序員都要認(rèn)真的編寫測試代碼,力求測試代碼能夠反映全部的意圖,此時,單元測試就是文檔!它不但能夠說明代碼的使用、功能,而且天然的與代碼同步, 更重要的是,它可以使你放心的維護(hù)你的代碼。
<!---->
●
???????
<!---->
現(xiàn)代編程語言對于文檔的支持。
在
Java
、
C#
、
Ruby
等新興的編程語言中,可以將代碼注釋當(dāng)作文檔使用。例如
Java
為文檔提供了很多支持——使用
HTML
標(biāo)簽、使用
@
標(biāo)記等,結(jié)合
Javadoc
命令就可以生成
HTML
格式的文檔。相比傳統(tǒng)的
Word
文檔,注釋作為文檔的優(yōu)勢是顯而易見的:
<!----> 1) ? <!----> 天然的與代碼同步,省去了很多同步的成本。
<!----> 2) ? <!----> 連接特性使得文檔更容易閱讀。
<!----> 3) ? <!----> 更加規(guī)范的文檔格式。
<!---->
●
???????
<!---->
源碼之前了無秘密。
當(dāng)測試、注釋、文檔都失去作用的時候,不要忘記,我們還有邏輯,還有代碼!代碼之前了無秘密。優(yōu)質(zhì)的代碼是說明應(yīng)用程序的最根本的方式,是程序員溝通的通用語言。
所以,請認(rèn)真的編寫每一行代碼!
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
