新聞中心
阿里妹導(dǎo)讀:為趕項(xiàng)目進(jìn)度欠下一堆技術(shù)債怎么辦?業(yè)務(wù)邏輯復(fù)雜,如何處理比較好?相似的功能要不要copy修改一下復(fù)用?怎么寫(xiě)代碼注釋?好代碼無(wú)論對(duì)個(gè)人還是團(tuán)隊(duì)都至關(guān)重要,然而要寫(xiě)好代碼卻是一件非常不容易的事情,需要長(zhǎng)期的經(jīng)驗(yàn)積累和學(xué)習(xí)。關(guān)于寫(xiě)好代碼,本文作者分享了6個(gè)入門(mén)的比較重要的點(diǎn),希望對(duì)同學(xué)們有所啟發(fā)。

成都創(chuàng)新互聯(lián)主營(yíng)祿勸網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,重慶APP開(kāi)發(fā),祿勸h5小程序定制開(kāi)發(fā)搭建,祿勸網(wǎng)站營(yíng)銷(xiāo)推廣歡迎祿勸等地區(qū)企業(yè)咨詢(xún)
寫(xiě)了多年的代碼,始終覺(jué)得如何寫(xiě)出干凈優(yōu)雅的代碼并不是一件容易的事情。按10000小時(shí)刻意訓(xùn)練的定理,假設(shè)每天8小時(shí),一個(gè)月20天,一年12個(gè)月,大概也需要5年左右的時(shí)間成為大師。其實(shí)我們每天的工作中真正用于寫(xiě)代碼的時(shí)間不可能有8個(gè)小時(shí),并且很多時(shí)候是在完成任務(wù),在業(yè)務(wù)壓力很大的時(shí)候,可能想要達(dá)到的目標(biāo)是如何盡快的使得功能work起來(lái),代碼是否干凈優(yōu)雅非??赡軟](méi)有能放在第一優(yōu)先級(jí)上,而是怎么快怎么來(lái)。
在這樣的情況下是非常容易欠下技術(shù)債的,時(shí)間長(zhǎng)了,這樣的代碼基本上無(wú)法維護(hù),只能推倒重來(lái),這個(gè)成本是非常高的。欠債要還,只是遲早的問(wèn)題,并且等到要還的時(shí)候還要賠上額外的不菲的利息。還債的有可能是自己,也有可能是后來(lái)的繼任者,但都是團(tuán)隊(duì)在還債。所以從團(tuán)隊(duì)的角度來(lái)看,寫(xiě)好代碼是一件非常有必要的事情。如何寫(xiě)出干凈優(yōu)雅的代碼是個(gè)很困難的課題,我沒(méi)有找到萬(wàn)能的solution,更多的是一些trade off,可以稍微討論一下。
代碼是寫(xiě)給人看的還是寫(xiě)給機(jī)器看的?
在大部分的情況下我會(huì)認(rèn)為代碼是寫(xiě)給人看的。雖然代碼最后的執(zhí)行者是機(jī)器,但是實(shí)際上代碼更多的時(shí)候是給人看的。我們來(lái)看看一段代碼的生命周期:開(kāi)發(fā) --> 單元測(cè)試 --> Code Review --> 功能測(cè)試 --> 性能測(cè)試 --> 上線(xiàn) --> 運(yùn)維、Bug修復(fù) --> 測(cè)試上線(xiàn) --> 退休下線(xiàn)。開(kāi)發(fā)到上線(xiàn)的時(shí)間也許是幾周或者幾個(gè)月,但是線(xiàn)上運(yùn)維、bug修復(fù)的周期可以是幾年。
在這幾年的時(shí)間里面,幾乎不可能還是原來(lái)的作者在維護(hù)了。繼任者如何能理解之前的代碼邏輯是極其關(guān)鍵的,如果不能維護(hù),只能自己重新做一套。所以在項(xiàng)目中我們經(jīng)常能見(jiàn)到的情況就是,看到了前任的代碼,都覺(jué)得這是什么垃圾,寫(xiě)的亂七八糟,還是我自己重寫(xiě)一遍吧。就算是在開(kāi)發(fā)的過(guò)程中,需要?jiǎng)e人來(lái)Code Review,如果他們都看不懂這個(gè)代碼,怎么來(lái)做Review呢。還有你也不希望在休假的時(shí)候,因?yàn)槠渌丝床欢愕拇a,只好打電話(huà)求助你。這個(gè)我印象極其深刻,記得我在工作不久的時(shí)候,一次回到了老家休假中,突然同事打電話(huà)來(lái)了,出現(xiàn)了一個(gè)問(wèn)題,問(wèn)我該如何解決,當(dāng)時(shí)電話(huà)還要收漫游費(fèi)的,非常貴,但是我還不得不支持直到耗光我的電話(huà)費(fèi)。
所以代碼主要還是寫(xiě)給人看的,是我們的交流的途徑。那些非常好的開(kāi)源的項(xiàng)目雖然有文檔,但是更多的我們其實(shí)還是看他的源碼,如果開(kāi)源項(xiàng)目里面的代碼寫(xiě)的很難讀,這個(gè)項(xiàng)目也基本上不會(huì)火。因?yàn)榇a是我們開(kāi)發(fā)人員交流的基本途徑,甚至可能口頭討論不清楚的事情,我們可以通過(guò)代碼來(lái)說(shuō)清楚。代碼的可讀性我覺(jué)得是第一位的。各個(gè)公司估計(jì)都有自己的代碼規(guī)范,遵循相關(guān)的規(guī)范保持代碼風(fēng)格的統(tǒng)一是第一步(推薦谷歌代碼規(guī)范[1]和微軟代碼規(guī)范[2])。規(guī)范里一般都包括了如何進(jìn)行變量、類(lèi)、函數(shù)的命名,函數(shù)要盡量短并且保持原子性,不要做多件事情,類(lèi)的基本設(shè)計(jì)的原則等等。另外一個(gè)建議是可以多參考學(xué)習(xí)一下開(kāi)源項(xiàng)目中的代碼。
KISS (Keep it simple and stupid)
一般大腦工作記憶的容量就是5-9個(gè),如果事情過(guò)多或者過(guò)于復(fù)雜,對(duì)于大部分人來(lái)說(shuō)是無(wú)法直接理解和處理的。通常我們需要一些輔助手段來(lái)處理復(fù)雜的問(wèn)題,比如做筆記、畫(huà)圖,有點(diǎn)類(lèi)似于在內(nèi)存不夠用的情況下我們借用了外存。
學(xué)CS的同學(xué)都知道,外存的訪(fǎng)問(wèn)速度肯定不如內(nèi)存訪(fǎng)問(wèn)速度。另外一般來(lái)說(shuō)在邏輯復(fù)雜的情況下出錯(cuò)的可能要遠(yuǎn)大于在簡(jiǎn)單的情況下,在復(fù)雜的情況下,代碼的分支可能有很多,我們是否能夠?qū)γ糠N情況都考慮到位,這些都有困難。為了使得代碼更加可靠,并且容易理解,最好的辦法還是保持代碼的簡(jiǎn)單,在處理一個(gè)問(wèn)題的時(shí)候盡量使用簡(jiǎn)單的邏輯,不要有過(guò)多的變量。
但是現(xiàn)實(shí)的問(wèn)題并不會(huì)總是那么簡(jiǎn)單,那么如何來(lái)處理復(fù)雜的問(wèn)題呢?與其借用外存,我更加傾向于對(duì)復(fù)雜的問(wèn)題進(jìn)行分層抽象。網(wǎng)絡(luò)的通信是一個(gè)非常復(fù)雜的事情,中間使用的設(shè)備可以有無(wú)數(shù)種(手機(jī),各種IOT設(shè)備,臺(tái)式機(jī),laptop,路由器,交換機(jī)...), OSI協(xié)議對(duì)各層做了抽象,每一層需要處理的情況就都大大地簡(jiǎn)化了。通過(guò)對(duì)復(fù)雜問(wèn)題的分解、抽象,那么我們?cè)诿總€(gè)層次上要解決處理的問(wèn)題就簡(jiǎn)化了。其實(shí)也類(lèi)似于算法中的divide-and-conquer, 復(fù)雜的問(wèn)題,要先拆解掉變成小的問(wèn)題,從而來(lái)簡(jiǎn)化解決的方法。
KISS還有另外一層含義,“如無(wú)必要,勿增實(shí)體” (奧卡姆剃刀原理)。CS中有一句 "All problems in computer science can be solved by another level of indirection", 為了系統(tǒng)的擴(kuò)展性,支持將來(lái)的一些可能存在的變化,我們經(jīng)常會(huì)引入一層間接層,或者增加中間的interface。在做這些決定的時(shí)候,我們要多考慮一下是否真的有必要。增加額外的一層給我們的好處就是易于擴(kuò)展,但是同時(shí)也增加了復(fù)雜度,使得系統(tǒng)變得更加不可理解。對(duì)于代碼來(lái)說(shuō),很可能是我這里調(diào)用了一個(gè)API,不知道實(shí)際的觸發(fā)在哪里,對(duì)于理解和調(diào)試都可能增加困難。
KISS本身就是一個(gè)trade off,要把復(fù)雜的問(wèn)題通過(guò)抽象和分拆來(lái)簡(jiǎn)單化,但是是否需要為了保留變化做更多的indirection的抽象,這些都是需要仔細(xì)考慮的。
DRY (Don't repeat yourself)
為了快速地實(shí)現(xiàn)一個(gè)功能,知道之前有類(lèi)似的,把代碼copy過(guò)來(lái)修改一下就用,可能是最快的方法。但是copy代碼經(jīng)常是很多問(wèn)題和bug的根源。有一類(lèi)問(wèn)題就是copy過(guò)來(lái)的代碼包含了一些其他的邏輯,可能并不是這部分需要的,所以可能有冗余甚至一些額外的風(fēng)險(xiǎn)。
另外一類(lèi)問(wèn)題就是在維護(hù)的時(shí)候,我們其實(shí)不知道修復(fù)了一個(gè)地方之后,還有多少其他的地方還需要修復(fù)。在我過(guò)去的項(xiàng)目中就出現(xiàn)過(guò)這樣的問(wèn)題,有個(gè)問(wèn)題明明之前做了修復(fù),過(guò)幾天另外一個(gè)客戶(hù)又提了類(lèi)似的問(wèn)題出現(xiàn)的另外的路徑上。相同的邏輯要盡量只出現(xiàn)在一個(gè)地方,這樣有問(wèn)題的時(shí)候也就可以一次性地修復(fù)。這也是一種抽象,對(duì)于相同的邏輯,抽象到一個(gè)類(lèi)或者一個(gè)函數(shù)中去,這樣也有利于代碼的可讀性。
是否要寫(xiě)注釋
個(gè)人的觀(guān)點(diǎn)是大部分的代碼盡量不要注釋。代碼本身就是一種交流語(yǔ)言,并且一般來(lái)說(shuō)編程語(yǔ)言比我們?nèi)粘J褂玫目谡Z(yǔ)更加的精確。在保持代碼邏輯簡(jiǎn)單的情況下,使用良好的命名規(guī)范,代碼本身就很清晰并且可能讀起來(lái)就已經(jīng)是一篇良好的文章。特別是OO的語(yǔ)言的話(huà),本身object(名詞)加operation(一般用動(dòng)詞)就已經(jīng)可以說(shuō)明是在做什么了。重復(fù)一下把這個(gè)操作的名詞放入注釋并不會(huì)增加代碼的可讀性。并且在后續(xù)的維護(hù)中,會(huì)出現(xiàn)修改了代碼,卻并不修改注釋的情況出現(xiàn)。在我做的很多Code Review中我都看到過(guò)這樣的情況。盡量把代碼寫(xiě)的可以理解,而不是通過(guò)注釋來(lái)理解。
當(dāng)然我并不是反對(duì)所有的注釋?zhuān)诠_(kāi)的API上是需要注釋的,應(yīng)該列出API的前置和后置條件,解釋該如何使用這個(gè)API,這樣也可以用于自動(dòng)產(chǎn)品API的文檔。在一些特殊優(yōu)化邏輯和負(fù)責(zé)算法的地方加上這些邏輯和算法的解釋還是非常有必要的。
一次做對(duì),不要相信以后會(huì)Refactoring
通常來(lái)說(shuō)在代碼中寫(xiě)上TODO,等著以后再來(lái)refactoring或者改進(jìn),基本上就不會(huì)再有以后了。我們可以去我們的代碼庫(kù)里面搜索一下TODO,看看有多少,并且有多少是多少年前的,我相信這個(gè)結(jié)果會(huì)讓你很驚訝(歡迎大家留言分享你查找之后的結(jié)果)。
盡量一次就做對(duì),不要相信以后還會(huì)回來(lái)把代碼refactoring好。人都是有惰性的,一旦完成了當(dāng)前的事情,move on之后再回來(lái)處理這些概率就非常小了,除非下次真的需要修改這些代碼。如果說(shuō)不會(huì)再回來(lái),那么這個(gè)TODO也沒(méi)有什么意義。如果真的需要,就不要留下這個(gè)問(wèn)題。我見(jiàn)過(guò)有的人留下了一個(gè)TODO,throw了一個(gè)not implemented的exception,然后幾天之后其他同學(xué)把這個(gè)代碼帶上線(xiàn)了,直接掛掉的情況。盡量不要TODO, 一次做好。
是否要寫(xiě)單元測(cè)試?
個(gè)人的觀(guān)點(diǎn)是必須,除非你只是做prototype或者快速迭代扔掉的代碼。
Unit tests are typically automated tests written and run by software developers to ensure that a section of an application (known as the "unit") meets its design and behaves as intended. In procedural programming, a unit could be an entire module, but it is more commonly an individual function or procedure. In object-oriented programming, a unit is often an entire interface, such as a class, but could be an individual method.
From Wikipedia
單元測(cè)試是為了保證我們寫(xiě)出的代碼確實(shí)是我們想要表達(dá)的邏輯。當(dāng)我們的代碼被集成到大項(xiàng)目中的時(shí)候,之后的集成測(cè)試、功能測(cè)試甚至e2e的測(cè)試,都不可能覆蓋到每一行的代碼了。如果單元測(cè)試做的不夠,其實(shí)就是在代碼里面留下一些自己都不知道的黑洞,哪天調(diào)用方改了一些東西,走到了一個(gè)不常用的分支可能就掛掉了。我之前帶的項(xiàng)目中就出現(xiàn)過(guò)類(lèi)似的情況,代碼已經(jīng)上線(xiàn)幾年了,有一次稍微改了一下調(diào)用方的參數(shù),覺(jué)得是個(gè)小改動(dòng),但是上線(xiàn)就掛了,就是因?yàn)橛龅搅酥案緵](méi)有人測(cè)試過(guò)的分支。單元測(cè)試就是要保證我們自己寫(xiě)的代碼是按照我們希望的邏輯實(shí)現(xiàn)的,需要盡量的做到比較高的覆蓋,確保我們自己的代碼里面沒(méi)有留下什么黑洞。關(guān)于測(cè)試,我想單獨(dú)開(kāi)一篇討論,所以就先簡(jiǎn)單聊到這里。
要寫(xiě)好代碼確實(shí)是已經(jīng)非常不容易的事情,需要考慮正確性、可讀性、魯棒性、可測(cè)試性、可以擴(kuò)展性、可以移植性、性能。前面討論的只是個(gè)人覺(jué)得比較重要的入門(mén)的一些點(diǎn),想要寫(xiě)好代碼需要經(jīng)過(guò)刻意地考慮和練習(xí)才能真正達(dá)到目標(biāo)!
相關(guān)鏈接
[1]https://google.github.io/styleguide/[2]https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
網(wǎng)頁(yè)標(biāo)題:如何寫(xiě)好代碼,分享6個(gè)入門(mén)的比較重要的點(diǎn)
轉(zhuǎn)載源于:http://fisionsoft.com.cn/article/cdodsoe.html


咨詢(xún)
建站咨詢(xún)
