從去年到今年,陸陸續(xù)續(xù)看完了《代碼大全》、《重構(gòu)》、《代碼整潔之道》、《程序員修煉之道》以及《The Art of Readable Code》,獲益匪淺。下面就分享幾條我贊同并信奉的編程哲學(xué),順便廢話幾句。 本文適合有一定編程經(jīng)驗(yàn)的讀者閱讀,高手請(qǐng)輕噴:) 代碼是寫(xiě)給人看的(Coding for Reading)請(qǐng)先思考,評(píng)價(jià)一段代碼優(yōu)劣最重要的標(biāo)準(zhǔn)是什么? 有個(gè)著名的圖,相信大家都見(jiàn)過(guò),講的是Code Review時(shí)被罵WTF的次數(shù)越少,代碼的質(zhì)量就越高。這雖然有點(diǎn)無(wú)厘頭,但是卻不無(wú)道理。 《The Art of Readable Code》中告訴我們,評(píng)價(jià)一段代碼的質(zhì)量的最佳標(biāo)準(zhǔn)是可讀性,即別人理解代碼意圖所需要的時(shí)間。 可讀性有什么用,我寫(xiě)出一段代碼,能完成目標(biāo),能通過(guò)測(cè)試不就行了嗎? 不管你是團(tuán)隊(duì)開(kāi)發(fā),還是一個(gè)人單干,只要項(xiàng)目還在運(yùn)作,代碼總是要有人來(lái)維護(hù)的。如果有一天你不在或者離開(kāi)了,別人應(yīng)該能很輕松的看懂你的代 碼,而不是猜來(lái)猜去,最后還要來(lái)問(wèn)你,甚至棄用整段代碼重新再寫(xiě)。作為一個(gè)負(fù)責(zé)任的開(kāi)發(fā)者,應(yīng)該尊重自己的勞動(dòng)成果,也尊重別人的時(shí)間。 當(dāng)你寫(xiě)下一段代碼時(shí),腦子里有清晰的邏輯(比如這里必須這么做,因?yàn)椤@里不能那么做,因?yàn)椤6鴦e人看這段代碼時(shí),腦中是一片空白(這 個(gè)變量是干什么的?為什么要多加一層判斷?這個(gè)數(shù)為什么是 2 不是 3 ?),除非能完整復(fù)現(xiàn)你當(dāng)時(shí)腦中的邏輯,否則就不能理解這段代碼。 所以我們應(yīng)該做的是,把寫(xiě)代碼時(shí)腦中想到的邏輯的每一個(gè)細(xì)節(jié),盡可能地寫(xiě)到代碼以及注釋中,從而幫助后來(lái)的閱讀者快速?gòu)?fù)原整個(gè)邏輯。簡(jiǎn)單來(lái)說(shuō)可以有以下的方法: 用常量代替魔術(shù)數(shù)在一段代碼中出現(xiàn) if ( level > 3 ) ...的時(shí)候,也許你會(huì)想:為什么是 3 不是 4 呢?這就是所謂的魔術(shù)數(shù)。通過(guò)上下文也許你可以判斷出這個(gè) 3 是指最高等級(jí),但是可能這段代碼中 3 出現(xiàn)了好幾次。你會(huì)迷惑:它們是不是都是一個(gè)意思?當(dāng)我要把最高等級(jí)改成 4 的時(shí)候,是不是應(yīng)該修改所有的 3 ? 如果代碼的作者定義一個(gè)常量MAX_LEVEL = 3,同時(shí)還有一個(gè)常量USERS_PER_PAGE = 3,并在不同的地方使用不同的常量,就不會(huì)有這樣的混淆了。 使用富含信息的類(lèi)名、變量名和函數(shù)名doit(); $return = $str2 . ':' . $str3; return $return; 這樣的代碼,簡(jiǎn)直就是人工混淆過(guò)的。你會(huì)發(fā)現(xiàn)這些代碼根本不能幫助你理解代碼的含義。 也許你該寫(xiě)成這樣? handleError(); $error_message = $error_code . ':' . $error_status; return $error_message; 在注釋中說(shuō)明一段代碼存在的原因,而不是行為$('#item').html(''); //清空item的內(nèi)容 這樣的注釋有意義么?用自然語(yǔ)言重新描述一次代碼的行為,除了徒增維護(hù)時(shí)的工作量外沒(méi)有任何價(jià)值。你應(yīng)該說(shuō)明為什么這樣做,以供別人看到這段代碼時(shí)明白你是怎么想的,并決定如何修改或者對(duì)待這段代碼。 $('#item').html(''); //先清空容器的內(nèi)容,否則可能導(dǎo)致內(nèi)容重復(fù) 寫(xiě)得越少越好(Less is More)這個(gè)規(guī)則的使用性太強(qiáng)了,我簡(jiǎn)單說(shuō)說(shuō)減少邏輯層次和縮小函數(shù)體這兩個(gè)方面吧。 減少邏輯層次當(dāng)邏輯層次超過(guò)三層時(shí),理解這段代碼的難度會(huì)急劇上升。我相信誰(shuí)也不喜歡去讀一個(gè)n層括號(hào)的表達(dá)式,或者面對(duì)n層縮進(jìn)的條件判斷/循環(huán)。 對(duì)于復(fù)雜的表達(dá)式,通過(guò)提取中間變量來(lái)降低表達(dá)式的邏輯層次,保證每個(gè)表達(dá)式的邏輯層次不超過(guò)二層。 對(duì)于多層條件判斷,大多數(shù)情況可以用防御式編程將其簡(jiǎn)化成單層的條件判斷,盡早return或者exit。此外,單行的if-else判斷往往可以用三元操作符替換。如果判斷實(shí)在太多,也許你該重新設(shè)計(jì)一下結(jié)構(gòu)了。 縮小函數(shù)體記得有一位語(yǔ)言的創(chuàng)始人說(shuō)過(guò):“我不喜歡比我的頭還大的函數(shù)”。 事實(shí)上,大家都喜歡短小精干、一眼就能看到底的代碼。簡(jiǎn)潔明快的代碼有助于別人迅速理解代碼的意圖,也方便快速定位問(wèn)題。如果一個(gè)函數(shù)要滾動(dòng)屏幕才能看全,那你往往要不斷地來(lái)回滾動(dòng),并強(qiáng)迫自己記住一些信息,再返回去看另一部分,這樣做會(huì)非常累。 隨著函數(shù)體不斷膨脹,理解它所需要的時(shí)間隨之增加,出錯(cuò)的幾率也會(huì)大大提升。而且越大的函數(shù),可維護(hù)性和復(fù)用性越差。當(dāng)部分代碼邏輯需要修改時(shí),不能快速定位到要修改的位置,也難以確定函數(shù)體其他位置是否也需要對(duì)應(yīng)的修改。 當(dāng)函數(shù)尺寸失控時(shí),首先要想到的是,有沒(méi)有其他方法,用更少的代碼完成這個(gè)任務(wù)?能不能用正則表達(dá)式?能不能用查表法?有沒(méi)有內(nèi)置的庫(kù)函數(shù)可以利用? 面對(duì)一個(gè)無(wú)法再簡(jiǎn)化的流程,將其拆分成細(xì)粒度的步驟,將每個(gè)步驟的相關(guān)代碼分離出來(lái),提取成子函數(shù),再給子函數(shù)起一個(gè)漂亮的名字。這樣可以降低理解主流程的難度,在做修改時(shí)也可以通過(guò)函數(shù)名快速定位,而且因?yàn)橄嚓P(guān)的代碼都在一起,不容易漏改。 也許你會(huì)懷疑調(diào)用函數(shù)所造成的性能損失,我想說(shuō)現(xiàn)在這個(gè)時(shí)代,手機(jī)都馬上四核了…… 不要重復(fù)(Don't Repeat Yourself)看過(guò)《重構(gòu)》一書(shū)后,我看到代碼中任何重復(fù)的地方都如見(jiàn)眼中釘。 重復(fù)是萬(wàn)惡之源,當(dāng)你發(fā)現(xiàn)你在對(duì)代碼的不同部分進(jìn)行同樣的修改時(shí)就要警惕了。改的地方越多,就越可能出錯(cuò)。也許忘了改一個(gè)地方,也許錯(cuò)改或者刪除了周邊的代碼……永遠(yuǎn)不要讓這種事情發(fā)生! 將重復(fù)的代碼提煉成子函數(shù)$('#count').text( + $('#count').text() + 1 ); ... $('#count').text( + $('#count').text() + 1 ); ... 如果一段相同的代碼出現(xiàn)兩次,基本上你還會(huì)第三次用到它,所以很有必要將其提煉成子函數(shù)。這樣不僅可以減少代碼量,還可以降低維護(hù)的難度。 countPlusOne(); ... countPlusOne(); ... function countPlusOne() { $('#count').text( + $('#count').text() + 1 ); //以后修改只用改這里就好了 } 如果有幾段代碼很相似,往往可以提取其共性邏輯,使用不同的調(diào)用參數(shù)進(jìn)行區(qū)分。 countChange( +1 ); ... countChange( -1 ); ... function countChange( change ) { $('#count').text( + $('#count').text() + change ); } 用循環(huán)減少重復(fù)$item[ 'id' ] = $_POST[ 'id' ]; $item[ 'name' ] = $_POST[ 'name' ]; $item[ 'mail' ] = $_POST[ 'mail' ]; $item[ 'qq' ] = $_POST[ 'qq' ]; 如果有大塊邏輯雷同只有一兩個(gè)地方更改的代碼,往往可以用循環(huán)來(lái)解決: foreach ( array( 'id', 'name', 'mail', 'qq' ) as $key ) { $item[ $key ] = $_POST[ $key ]; } |
|
來(lái)自: rookie > 《技術(shù)帖》