代碼質(zhì)量隨想錄(四)――排版,不只是為了漂亮._第1頁
代碼質(zhì)量隨想錄(四)――排版,不只是為了漂亮._第2頁
已閱讀5頁,還剩8頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、代碼質(zhì)量隨想錄(四):排版,不只是為了漂亮作者:愛飛翔 發(fā)布時間:2012-06-15 21:18針對排版這個問題,不同的公司、團(tuán)隊都有自己的一套方案,有時網(wǎng)絡(luò)上也能下載到很多 大型的權(quán)威代碼規(guī)范,其中亦含有程序排版相關(guān)的規(guī)則,我也經(jīng)常與眾友人一起討論某個項目 所用的排版約定。在看到The Art of Readable Code 一書中有關(guān)此話題的章節(jié)時,我的感覺是,很難總結(jié)出一套萬用的宇宙排版律”來,多半要根據(jù)自身環(huán)境、團(tuán)隊和項目的特點來擬定,所給出的建議僅僅是參考,并不能強(qiáng)行照搬。1.功能相似的代碼,版式也應(yīng)相似public classPerformanceTester public s

2、tatic final TcpConnectionSimulator wifi =new TcpConnectionSimulator(500 /*以 Kbps 計量的吞吐量*/,80 /*以毫秒計的網(wǎng)絡(luò)延遲 */,200 /*包抖動*/,1 /*丟包百分比*/;public static final TcpConnectionSimulator t3Fiber =new TcpConnectionSimulator(45000 /*以 Kbps 計量的吞吐量*/,10 /*以毫秒計的網(wǎng)絡(luò)延遲 */,0 /*包抖動*/,0 /*丟包百分比*/;public static final TcpCo

3、nnectionSimulator cell =new TcpConnectionSimulator100 /*以 Kbps 計量的吞吐量*/,400 /*以毫秒計的網(wǎng)絡(luò)延遲 */,250 /*包抖動*/,5 /*丟包百分比*/;上面這個例子是 ARC 書中所舉的,我認(rèn)為很恰當(dāng)。該類的三個靜態(tài)字段功能類似,都指 代某種環(huán)境下的網(wǎng)絡(luò)模擬器,所以排版也應(yīng)該相似。每行都只寫一個實參,而且后面用行內(nèi)注 釋的形式解釋該實參的意思。在垂直方向上的對齊做得也很好:字段申明前面空2 格,實例化語句前面空 4 格,各實參前面空6 格(以上數(shù)字非實指,僅是舉例而已)。這樣要修改某個參數(shù),很快就能定位到它,而且以后

4、如果增加類似的字段,如badWIFI ,也可以比照這個格式來,便于維護(hù)。由以上范例還可引出一個問題,那就是在實例化或方法調(diào)用中,經(jīng)常會遇到一些孤立的魔 法數(shù)字(magic number ),如果確有必要為它起名,那么不妨執(zhí)行一個小的重構(gòu),以常量 來代替它。反之,如果是大段的硬數(shù)值,則不一定非要為每個值都起一個名字,例如:TcpConnectionSimulator wifi =newTcpConnectionSimulator(WIFI_KBPS_THROUGHPUT,WIFI_LATENCY,WIFI_JITTER,WIFI_PACKET_LOSS_PERCENT;這樣反而顯得累贅。不妨像上

5、例那樣采用行內(nèi)注釋的辦法來解釋這些硬值的意思。承上,ARC 的作者又推導(dǎo)出一條建議,就是將相似的方法調(diào)用參數(shù)注釋提取到一處,例 如:public classPerformanceTester / TcpConnectionSimulator(throughput, latency, jitter, packet_loss/ Kbps ms ms percentpublic static finalTcpConnectionSimulator wifi =new TcpConnectionSimulator(500, 80, 200, 1 ;public static final TcpConn

6、ectionSimulator t3Fiber =new TcpConnectionSimulator(45000, 10, 0, 0 ;public static final TcpConnectionSimulator cell =new TcpConnectionSimulator(100, 400, 250, 5 ;說實在的,以前在工作中還沒太重視這個問題,一來是覺得我在寫Javadoc 時一貫非常完備,出現(xiàn)這種情況時只需靠鼠標(biāo)懸停就可知道某個方法或構(gòu)造器的具體信息了;二來嘛,也 是想著如果使用大量數(shù)值的調(diào)用代碼多到無法管控,我可能會祭出配置文件這個大旗來,將它 們?nèi)考{入配置中了事。

7、所以關(guān)于以上例子中談到的這些問題,我覺得還是根據(jù)大家的具體實 踐來理解為好,不要機(jī)械地尋求一致。2.將大量相似的嵌套式、接續(xù)式調(diào)用邏輯整合到共用方法之中,即利于排版,又可凸顯 重要數(shù)據(jù)在測試用例等代碼中,經(jīng)常會出現(xiàn)類似下面這種狀況:/某受測類中:/ 將類似Doug Adams這樣的不完整稱呼進(jìn)行補(bǔ)全,擴(kuò)展為Mr. Douglas Adams的形式。/如若不能(查不到數(shù)據(jù)或無法補(bǔ)完),則于 error 參數(shù)中填充錯誤信息并返回空串。/此方法會置空錯誤信息接收參數(shù)。public String expandToFullName(DatabaseConnection conn,String parti

8、alName,ErrorMessageReceiver error./某測試方法中:DatabaseConnection connection.;ErrorMessageReceiver error=;assertEquals(expandToFullName(connection, Doug Adams ,error ,Mr. Douglas Adams;assertEquals(error.getMessage(,;assertEquals(expandToFullName(connection, Jake Brown ,error ,Mr. Jacob Brown III;assertE

9、quals(error.getMessage(,;assertEquals(expandToFullName(connection, No Such Guy “ ,error ,Illi .assertEquals(error.getMessage( ,no match found;assertEquals(expandToFullName(connection, John “, error ,Illi .assertEquals(error.getMessage( ,more than one result;這符合上面所說的量大” 形似”嵌套”等特征,而且諸如輸入字串、預(yù)期結(jié)果、預(yù)期錯誤消息

10、等重要的數(shù)據(jù),被埋沒于conn ection、error、getMessage(等技術(shù)細(xì)節(jié)之中。所以可以借由美化版式之機(jī)進(jìn)行重構(gòu):checkPartialToFull(Doug Adams , Mr. Douglas Adams,;checkPartialToFull( Jake Brown , Mr. Jake Brown III,;checkPartialToFull( No Such Guy, , no match found;checkPartialToFull( John , , more than one result;private void checkPartialToFull

11、(String partialName,String expectedFullName,String expectedErrorMessage / connection 已被提取為測試固件類的成員變量ErrorMessageReceiver error=;String actualFullName = expandToFullName(connection, partialName, error;assertEquals(expectedErrorMessage, error.getMessage(;assertEquals(expectedFullName , actualFullName;

12、如此一來一舉三得:既消除了重復(fù)代碼,同時美化了版式,凸顯了輸入字串、預(yù)期結(jié)果、 預(yù)期錯誤消息等重要數(shù)據(jù),順帶著還方便了后續(xù)測試數(shù)據(jù)的維護(hù)。這種藉由版式整理帶來的重 構(gòu),我看可以有!3.明智地使用縱向?qū)R來減少拼寫錯誤、厘清大量同組數(shù)據(jù)。我覺得這一條和第 1 條有重復(fù),其實也屬于類似功能的代碼應(yīng)具類似版式之意,不過既然 ARC作者將它單列,我想可能是為了強(qiáng)調(diào)縱向?qū)R的好處吧。/將 POST 參數(shù)中的屬性分別提取至各個局部變量中ServletRequest requests.;String details = request.getParameter(details;String location

13、 = request.getParameter(location;String hone = request.getParameter(phon;String email = request.getParameter(email;String url = request.getParameter(url;經(jīng)由縱向?qū)R,很容易看出第三個局部變量這行的錯誤:將變量名“phone”誤寫為“ hon e,參數(shù)名的“pho ne 則錯成了 ” phon?!傲硗?,在進(jìn)行結(jié)構(gòu)體數(shù)據(jù)、數(shù)組成員等這種同組數(shù)據(jù)排列時,也可以充分利用版式來厘清 每個元素的意義。ARC 的作者就大贊 wget 這個命令行工具在指定參

14、數(shù)結(jié)構(gòu)體時,代碼排列 地很工整。/非原文,小翔以 Java 形式改寫Object commands = /參數(shù)名,默認(rèn)值,類型 timeout,null, TIMEOUT , timestamping , defOpt.timestamp, BOOLEAN , tries, defOpt.tryCount, NUMBER , useproxy, defOpt.useProxy, BOOLEAN , useragent,null, USER_AGENT ;這一條建議如果與第 1 條合并起來說,那就是:任務(wù)相似的代碼塊應(yīng)該具有相似的輪廓 (ARC 的作者叫它 silhouette ),如行數(shù)、縮進(jìn)

15、、縱向?qū)R等。4.使用適當(dāng)空行與注釋,將代碼按功能分段有時候經(jīng)常在考慮代碼與散文或詩的聯(lián)系,如果從隱喻(metaphor)的觀點來看,的確有相似性:都是信息的載體,都可以用一定的段落來整合文意。要說區(qū)別嘛,前者服務(wù)于軟件需求,后者服 務(wù)于社會關(guān)系。前者為了向更低階的執(zhí)行機(jī)制去接合,所以更加注重語法格式。我可不是第一個進(jìn)行 這種思維比擬的人,記得臺灣的技術(shù)暢銷書作者侯捷先生(侯俊杰)就曾寫過一本左手程序右手詩的書。class FrontendServer public :Fronten dServer(;void ViewProfile(HttpRequest*request;void Open

16、Database(string location, string user;void SaveProfile(HttpRequest*request;string ExtractQueryParam(HttpRequest* request, string param;void ReplyOK(HttpRequest* request, string html;void FindFriends(HttpRequest*request;void ReplyNotFound(HttpRequest*request, string error;void CloseDatabase(string lo

17、cation; FrontendServer(;上面的代碼挺蝸居的,如果加上適當(dāng)?shù)目招信c說明,就顯得清晰多了。class FrontendServer public :Fronten dServer(; FrontendServer(;/與用戶配置相關(guān)的處理函數(shù)void ViewProfile(HttpRequest*request;void SaveProfile(HttpRequest* request;void FindFriends(HttpRequest* request;/回覆及應(yīng)答工具函數(shù)string ExtractQueryParam(HttpRequest* request,

18、 string param;void ReplyOK(HttpRequest* request, string html;void ReplyNotFound(HttpRequest* request, string error;/數(shù)據(jù)庫操作工具函數(shù)void OpenDatabase(string location, string user;void CloseDatabase(string location;上述類將聲明區(qū)按照構(gòu)建子/析構(gòu)子、社交功能函數(shù)、工具函數(shù)這個標(biāo)準(zhǔn)劃分為的三大思維區(qū)段,工具函數(shù)區(qū)又按題材劃分為消息操作與數(shù)據(jù)庫操作兩小段。這樣一來,以后再要維護(hù) 這份聲明代碼就會很清爽了

19、。同理,如果聲明一個集合類的接口,也應(yīng)該按照增、刪、改、查”等概念來將 API 劃分為若干小組,以便幫助代碼閱讀者理順?biāo)悸贰>退闶窃诹魉降臉I(yè)務(wù)代碼中,也可以用段落來襯托出邏輯的起、承、轉(zhuǎn)、合”。/導(dǎo)入用戶電子郵件賬戶中聯(lián)系人,同本產(chǎn)品中已有的聯(lián)系人相比對。/然后展示正在使用本產(chǎn)品但未與用戶建立朋友關(guān)系的聯(lián)絡(luò)人列表。public ListDataModel suggestNewFriends(User user,Password emailPasswordSocialCircle friends = user.friends(;Emails friendEmails = friend.dump

20、AIIEmails(;Contacts contacts = importContacts(user.email, emailPassword;Emails contactEmails = contacts.extractAIIEmails(;Emails productUserEmails = UserDataCenter.selectEmails(contactEmails;Emails suggestedFriends = productUserEmails.subtract(friendEmails;ListDataModel displayModel = new ListDataMo

21、del(user,friends,suggestedFriends;return displayModel;上面的代碼給人的壓迫感很強(qiáng)列,沒有思維喘息的機(jī)會。不如把注釋拆解,按其邏輯將代 碼分成小段,為每一段冠以簡短標(biāo)題。public ListDataModel suggestNewFriends(User user,Password emailPassword/取得當(dāng)前用戶全部朋友的郵件地址SocialCircle friends = user.friends(;Emails friendEmails = friend.dumpAIIEmails(;/引入當(dāng)前用戶電子郵件賬戶中的聯(lián)系人Con

22、tacts contacts = importContacts(user.emaiI, emailPassword;Emails contactEmails = contacts.extractAIIEmaiIs(;/找出正在使用本產(chǎn)品但尚未與本用戶建立朋友關(guān)系的聯(lián)系人Emails productUserEmails = UserDataCenter.selectEmails(contactEmails;Emails suggestedFriends = productUserEmails.subtract(friendEmails;/返回待顯示列表的數(shù)據(jù)模型ListDataModel dis

23、playModel = new ListDataModel(user,friends,suggestedFriends;return displayModel;欣賞一下上面這段代碼吧,每小段以一句概括性的注釋引領(lǐng),然后是兩句實現(xiàn)代碼,排列 得非常整齊,代碼的閱讀者根據(jù)此的版式,很容易就能抓住代碼的思維走向:“2(獲取朋友列表)-2 (獲取聯(lián)系人郵箱)-2 (找出潛在友人)-2 (返回數(shù)據(jù)模型)”。怎么樣,是不是有點 兒起、承、轉(zhuǎn)、合”的意思了?(大誤)由上例可見,適當(dāng)?shù)剡M(jìn)行代碼分段并通過注釋來充當(dāng)代碼段的概括語,有助于梳理代碼閱 讀者的思路,也有助于代碼修改、維護(hù)和后續(xù)查錯。比如想做一個向未使

24、用本社交網(wǎng)站的電郵聯(lián)絡(luò)人發(fā)送邀請”的功能,掃一眼上述這段清晰排版的代碼,大家立刻就能看出,只需要寫 好測試用例,復(fù)制一份suggestNewFriends 的代碼,把 selectEmails 改成excludeEmails,就能找到這些潛在的被邀請人了。給新的方法起個名字,叫in viteC on tacts ,刪去多余的程序,然后通過重構(gòu)提取一下共用代碼,再確保測試無誤,就可以收工了。思路順了,編碼的過程自然也就更加流暢了。好了,小小總結(jié)一下吧。其實代碼排版這種略帶個人化的東西,不僅僅是讓代碼看起來更 漂亮,其根本目的還是著眼于代碼的可讀性,要有助于代碼的理解、維護(hù)、糾錯。具體到執(zhí)行 層面

25、,除了可以參考上述 4 條建議外,還要注意兩方面的問題。第一個問題,ARC 的作者也提到了,那就是很多朋友對代碼排版有排斥心理,不愿意認(rèn) 真排版。有一部分原因是怕浪費時間,還有就是擔(dān)心代碼管理系統(tǒng)會將排版后的代碼與排版之 前的代碼判定為兩份截然不同的程序,在版本比對時導(dǎo)致滿屏的diff,非常難看。其實,在現(xiàn)有的成熟 IDE 之中(抑或各位 Geek 們慣用的文本編輯器之中)已經(jīng)有非常完備的功能來支 援代碼版式的調(diào)整了。比如Eclipse、Netbeans等開發(fā)環(huán)境,都可以把版式定義文件導(dǎo)出為xml 等數(shù)據(jù)格式,到了陌生的環(huán)境時,只需導(dǎo)入即可。而且代碼排版一旦確定,就可以一次性 地更改所有項目源

26、碼的版式然后提交,這樣就可以避免在版本比對時顯示過多的修改提示了。第二個問題就是應(yīng)該在必要的范圍內(nèi)保持代碼排版的一致性。雖然我剛也說了,代碼排版沒有絕對的真理,不過,它卻應(yīng)該有一個相對的底線。在公司與公司之間、團(tuán)隊與團(tuán)隊之間,的確 沒有必要強(qiáng)行要求一致的版式。例如我們不宜妄自菲薄,說I 記或 G 社的代碼排得如何如何漂亮,同時也不能過分地自高自大,說自己團(tuán)隊的版式是天下最美觀、最養(yǎng)眼的。但是,如果 具體到某個項目,尤其是中小型項目里面,那么就要想方設(shè)法達(dá)成一致的版式規(guī)范了,否則將 會給代碼的閱讀、理解與維護(hù)造成不必要的障礙。為此,項目組的成員應(yīng)該富有的妥協(xié)精神,在堅持個人風(fēng)格這個問題上稍作讓步

27、,以求達(dá)成大家對代碼版式的共識。比如,小翔在個人項目或由我?guī)ш牭捻椖恐?,通常使用以下版式:public class MyArrayList extends MyAbstractList implementsMyCollection/靜態(tài)部分在前:/靜態(tài)內(nèi)部類型區(qū)。同區(qū)成員按存取級別排序,高者在前。/* *列表容量參數(shù)。*/public static classCapacityOptions/* 初始容量。*/private final intinitialElementCount;/*擴(kuò)容時新增的容量與擴(kuò)容前容量之比。*/private final intexpandRatio;/靜態(tài)初始化塊與靜態(tài)字段區(qū)。private static final Map commonCapacityOptions=.staticcom mon CapacityOpti ons.put(normal,new CapacityOptions(12,1;/靜態(tài)方法區(qū)。/*從既有數(shù)組中構(gòu)建列表。* param elements 用以構(gòu)建的數(shù)組,不能為 null* return 構(gòu)建好的列表*/public staticList create(Object elements/動態(tài)部分在后:/動態(tài)內(nèi)部類型區(qū)。public classMylterator

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論