原創(chuàng)|行業(yè)資訊|編輯:王香|2017-04-17 13:55:19.000|閱讀 477 次
概述:漂亮整潔的代碼不僅給人清爽舒適的感覺(jué),也能有效提高程序員的工作效率,毫無(wú)疑問(wèn),在這個(gè)顏值擔(dān)當(dāng)?shù)臅r(shí)代,代碼也要看臉了,所以如何寫得一手漂亮整潔的代碼是程序員們特別需要get的技能。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
漂亮整潔的代碼不僅給人清爽舒適的感覺(jué),也能有效提高程序員的工作效率,毫無(wú)疑問(wèn),在這個(gè)顏值擔(dān)當(dāng)?shù)臅r(shí)代,代碼也要看臉了,所以如何寫得一手漂亮整潔的代碼是程序員們特別需要get的技能。
代碼整潔的大前提
代碼大部分時(shí)候是用來(lái)維護(hù)的,而不是用來(lái)實(shí)現(xiàn)功能的
這個(gè)原則適用于大部分的工程。我們的代碼,一方面是編譯好讓機(jī)器執(zhí)行,完成功能需求;另一方面,是寫給身邊的隊(duì)友和自己看的,需要長(zhǎng)期維護(hù),而且大部分項(xiàng)目都不是朝生夕死的短命鬼。
對(duì)清晰好看代碼的追求精神,比所有的技巧都要重要。
優(yōu)秀的代碼大部分是可以自描述的,好于文檔和注釋
當(dāng)你翻看很多開(kāi)源代碼時(shí),會(huì)發(fā)現(xiàn)注釋甚至比我們自己寫的項(xiàng)目都少,但是卻能看的很舒服。當(dāng)讀完源碼時(shí),很多功能設(shè)計(jì)就都清晰明了了。通過(guò)仔細(xì)斟酌的方法命名、清晰的流程控制,代碼本身就可以拿出來(lái)當(dāng)作文檔使用,而且它永遠(yuǎn)不會(huì)過(guò)期。
相反,注釋不能讓寫的爛的代碼變的更好。如果別人只能依靠注釋讀懂你的代碼的時(shí)候,你一定要反思代碼出現(xiàn)了什么問(wèn)題(當(dāng)然,這里不是說(shuō)大家不要寫注釋了)。
說(shuō)下比較適合寫注釋的兩種場(chǎng)景:
設(shè)計(jì)模式只是手段,代碼清晰才是目的
之前見(jiàn)過(guò)一些所謂“高手”的代碼都比較抽象,各種工廠、各種繼承。想找到一個(gè)實(shí)現(xiàn)總是要山路十八彎,一個(gè)工程里大部分的類是抽象類或者接口,找不到一兩句實(shí)現(xiàn)的代碼,整個(gè)讀起代碼來(lái)很不順暢。我跟他聊起來(lái)的時(shí)候,他的主要立場(chǎng)是:保留合適的擴(kuò)展點(diǎn),克服掉所有的硬編碼。
其實(shí)在我看來(lái),也許他的代碼被“過(guò)度設(shè)計(jì)”了。首先必須要承認(rèn)的是,在同一個(gè)公司工作的同事,水平是參差不齊的。無(wú)論你用了如何高大上的設(shè)計(jì),如果大多數(shù)人都不能理解你的代碼或者讀起來(lái)很費(fèi)勁的話,其實(shí)這是一個(gè)失敗的設(shè)計(jì)。
當(dāng)你的系統(tǒng)內(nèi)大部分抽象只有一個(gè)實(shí)現(xiàn)的時(shí)候,要好好思考一下,是不是設(shè)計(jì)有點(diǎn)過(guò)度了,清晰永遠(yuǎn)是第一準(zhǔn)則。
代碼整潔的常見(jiàn)手段
code review
很多大公司會(huì)用git的pull request機(jī)制來(lái)做code review。我們重點(diǎn)應(yīng)該review什么?是代碼的格式、業(yè)務(wù)邏輯還是代碼風(fēng)格?我想說(shuō)的是,凡是能通過(guò)機(jī)器檢查出來(lái)的事情,無(wú)需通過(guò)人。比如換行、注釋、方法長(zhǎng)度、代碼重復(fù)等。除了基本功能需求的邏輯合理沒(méi)有bug外,我們更應(yīng)該關(guān)注代碼的設(shè)計(jì)與風(fēng)格。比如,一段功能是不是應(yīng)該屬于一個(gè)類、是不是有很多相似的功能可以抽取出來(lái)復(fù)用、代碼太過(guò)冗長(zhǎng)難懂等等。
我個(gè)人非常推崇集體code review,因?yàn)楹芏鄷r(shí)候,組里相對(duì)高級(jí)的工程師能夠一眼發(fā)現(xiàn)代碼存在較大設(shè)計(jì)缺陷,提出改進(jìn)意見(jiàn)或者重構(gòu)方式。我們可以在整個(gè)小組內(nèi)形成一個(gè)好的文化傳承和風(fēng)格統(tǒng)一,并且很大程度上培養(yǎng)了大家對(duì)clean code的熱情。
勤于重構(gòu)
好的代碼,一般都不是一撮而就的。即使一開(kāi)始設(shè)計(jì)的代碼非常優(yōu)秀,隨著業(yè)務(wù)的快速迭代,也可能被改的面目全非。
為了避免重構(gòu)帶來(lái)的負(fù)面影響(delay需求或者帶來(lái)bug),我們需要做好以下的功課:
靜態(tài)檢查
現(xiàn)在市面上有很多代碼靜態(tài)檢查的工具,也是發(fā)現(xiàn)bug和風(fēng)格不好的比較容易的方式。可以與發(fā)布系統(tǒng)做集成,強(qiáng)制把主要問(wèn)題修復(fù)掉才可以上線。目前美團(tuán)點(diǎn)評(píng)技術(shù)團(tuán)隊(duì)內(nèi)部的研發(fā)流程中已經(jīng)普遍接入了Sonar質(zhì)量管理平臺(tái)。
代碼整潔的常見(jiàn)技巧
單一職責(zé)
這是整潔代碼的最重要也是最基本的原則了。簡(jiǎn)單來(lái)講,大到一個(gè)module、一個(gè)package,小到一個(gè)class、一個(gè)method乃至一個(gè)屬性,都應(yīng)該承載一個(gè)明確的職責(zé)。要定義的東西,如果不能用一句話描述清楚職責(zé),就把它拆掉。
我們平時(shí)寫代碼時(shí),最容易犯的錯(cuò)誤是:一個(gè)方法干了好幾件事或者一個(gè)類承載了許多功能。
先來(lái)聊聊方法的問(wèn)題。個(gè)人非常主張把方法拆細(xì),這是復(fù)用的基礎(chǔ)。如果方法干了兩件事情,很有可能其中一個(gè)功能的其他業(yè)務(wù)有差別就不好重用了。另外語(yǔ)義也是不明確的。經(jīng)常看到一個(gè)get()方法里面竟然修改了數(shù)據(jù),這讓使用你方法的人情何以堪?如果不點(diǎn)進(jìn)去看看實(shí)現(xiàn),可能就讓程序陷入bug,讓測(cè)試陷入麻煩。
再來(lái)聊聊類的問(wèn)題。我們經(jīng)常會(huì)看到“又臭又長(zhǎng)”的service/biz層的代碼,里面有幾十個(gè)方法,干什么的都有:既有增刪改查,又有業(yè)務(wù)邏輯的聚合。每次找到一個(gè)方法都費(fèi)勁。不屬于一個(gè)領(lǐng)域或者一個(gè)層次的功能,就不要放到一起。
我們 team 在code review中,最常被批評(píng)的問(wèn)題,就是一個(gè)方法應(yīng)該歸屬于哪個(gè)類。
優(yōu)先定義整體框架
我寫代碼的時(shí)候,比較喜歡先去定義整體的框架,就是寫很多空實(shí)現(xiàn),來(lái)把整體的業(yè)務(wù)流程穿起來(lái)。良好的方法簽名,用入?yún)⒑统鰠?lái)控制流程。這樣能夠避免陷入業(yè)務(wù)細(xì)節(jié)無(wú)法自拔。在腦海中先定義清楚流程的幾個(gè)階段,并為每個(gè)階段找到合適的方法/類歸屬。
這樣做的好處是,閱讀你代碼的人,無(wú)論讀到什么深度,都可以清晰地了解每一層的職能,如果不care下一層的實(shí)現(xiàn),完全可以跳過(guò)不看,并且方法的粒度也會(huì)恰到好處。
簡(jiǎn)而言之,我比較推崇寫代碼的時(shí)候“廣度優(yōu)先”而不是“深度優(yōu)先”,這和我讀代碼的方式是一致的。當(dāng)然,這件事情跟個(gè)人的思維習(xí)慣有一定的關(guān)系,可能對(duì)抽象思維能力要求會(huì)更高一些。如果開(kāi)始寫代碼的時(shí)候這些不夠清晰,起碼要通過(guò)不斷地重構(gòu),使代碼達(dá)到這樣的成色。
清晰的命名
老生常談的話題,這里不展開(kāi)講了,但是必須要mark一下。有的時(shí)候,我思考一個(gè)方法命名的時(shí)間,比寫一段代碼的時(shí)間還長(zhǎng)。原因還是那個(gè)邏輯:每當(dāng)你寫出一個(gè)類似于”temp”、”a”、”b”這樣變量的時(shí)候,后面每一個(gè)維護(hù)代碼的人,都需要用幾倍的精力才能理順。
并且這也是代碼自描述最重要的基礎(chǔ)。
避免過(guò)長(zhǎng)參數(shù)
如果一個(gè)方法的參數(shù)長(zhǎng)度超過(guò)4個(gè),就需要警惕了。一方面,沒(méi)有人能夠記得清楚這些函數(shù)的語(yǔ)義;另一方面,代碼的可讀性會(huì)很差;最后,如果參數(shù)非常多,意味著一定有很多參數(shù),在很多場(chǎng)景下,是沒(méi)有用的,我們只能構(gòu)造默認(rèn)值的方式來(lái)傳遞。
解決這個(gè)問(wèn)題的方法很簡(jiǎn)單,一般情況下我們會(huì)構(gòu)造paramObject。用一個(gè)struct或者一個(gè)class來(lái)承載數(shù)據(jù),一般這種對(duì)象是value object,不可變對(duì)象。這樣,能極大程度提高代碼的可復(fù)用性和可讀性。在必要的時(shí)候,提供合適的build方法,來(lái)簡(jiǎn)化上層代碼的開(kāi)發(fā)成本。
避免過(guò)長(zhǎng)方法和類
一個(gè)類或者方法過(guò)長(zhǎng)的時(shí)候,讀者總是很崩潰的。簡(jiǎn)單地把方法、類和職責(zé)拆細(xì),往往會(huì)有立竿見(jiàn)影的成效。以類為例,拆分的維度有很多,常見(jiàn)的是橫向/縱向。
例如,如果一個(gè)service,處理的是跟一個(gè)庫(kù)表對(duì)象相關(guān)的所有邏輯,橫向拆分就是根據(jù)業(yè)務(wù),把建立/更新/修改/通知等邏輯拆到不同的類里去;而縱向拆分,指的是把數(shù)據(jù)庫(kù)操作/MQ操作/Cache操作/對(duì)象校驗(yàn)等,拆到不同的對(duì)象里去,讓主流程盡量簡(jiǎn)單可控,讓同一個(gè)類,表達(dá)盡量同一個(gè)維度的東西。
讓相同長(zhǎng)度的代碼段表示相同粒度的邏輯
這里想表達(dá)的是,盡量多地去抽取private方法,讓代碼具有自描述的能力。舉個(gè)簡(jiǎn)單的例子
public void doSomeThing(Map params1,Map params2){ Do1 do1 = getDo1(params1); Do2 do2 = new Do2(); do2.setA(params2.get("a")); do2.setB(params2.get("b")); do2.setC(params2.get("c")); mergeDO(do1,do2); } private void getDo1(Map params1); private void mergeDo(do1,do2){...};
類似這種代碼,在業(yè)務(wù)代碼中隨處可見(jiàn)。獲取do1是一個(gè)方法,merge是一個(gè)方法,但獲取do2的代碼卻在主流程里寫了。這種代碼,流程越長(zhǎng),讀起來(lái)越累。很多人讀代碼的邏輯,是“廣度優(yōu)先”的。先讀懂主流程,再去看細(xì)節(jié)。類似這種代碼,如果能夠把構(gòu)造do2的代碼,提取一個(gè)private 方法,就會(huì)舒服很多。
以上這些優(yōu)化管理代碼的方法能夠有效使我們的代碼看起來(lái)更清爽更漂亮,還是那句話,這絕不僅僅是顏值問(wèn)題,這更是一個(gè)工作效率的問(wèn)題,不想在冗雜的代碼里掙扎就不妨嘗試一下上面的方法吧,也許你會(huì)發(fā)現(xiàn)something amazing.
微信關(guān)注公眾號(hào):Aspose 滿足一切文檔需求
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn