DRY 是一個最簡單的法則,也是最容易被理解的。 但它也可能是最難被應(yīng)用的(因為要做到這樣,我們需要在泛型設(shè)計上做相當(dāng)?shù)呐Γ? 這并不是一件容易的事)。
它意味著,當(dāng)我們在兩個或多個地方發(fā)現(xiàn)一些相似的代碼的時候, 我們需要把他們的共性抽象出來,形成一個唯一的新方法,并且改變現(xiàn)有的代碼, 讓他們以一些合適的參數(shù)調(diào)用這個新的方法。
DRY 這一法則可能是編程界中最通用的法則了。 目前為止,應(yīng)該沒有哪個程序員對這一法則存有異議。 但是,我們卻能發(fā)現(xiàn),一些程序在編寫單元測試時忘記了這一法則: 讓我們想象一下,當(dāng)你改變一個類的若干接口,如果你沒有使用DRY, 那么,那些通過調(diào)用一系例類的接口的unit test的程序,都需要被手動的更改。
比如:如果你的unit test的諸多test cases中沒有使用一個標(biāo)準(zhǔn)共有的構(gòu)造類的方法, 而是每個test case自己去構(gòu)造類的實例,那么, 當(dāng)類的構(gòu)造函數(shù)被改變時,你需要修改多少個test cases啊。 這就是不使用DRY法則所帶來的惡果。
至少,我們有下面三個不錯的理由要求程序員們寫下短小的方法。
代碼會變得更容易閱讀。
代碼會變得更容易重用(短方法可以減少代碼間的耦合程度)
代碼會變得更容易測試。
使用不錯的統(tǒng)一的命名規(guī)范可以讓你的程序變得更容易閱讀和維護(hù), 當(dāng)一個類,一個函數(shù),一個變量的名字達(dá)到了那種可以“望文生義”的境界時, 我們就可以少一些文檔,少一些溝通。
一個類,一個職責(zé),這類規(guī)則可以參考一下類的SOLID 法則。 但我們這里強(qiáng)調(diào)的不是一種單一的職責(zé),而是一個正確的職責(zé)。 如果你有一個類叫Customer,我們就不應(yīng)該讓這個類有sales的方法, 我們只能讓這個類有和Customer有最直接關(guān)系的方法。
把代碼組織起來有兩組層次。
物理層組織:無論你使用什么樣的目錄,包(package)或命名空間(namespace)等的結(jié)構(gòu), 你需要把你的類用一種標(biāo)準(zhǔn)的方法組織起來,這樣可以方便查找。 這是一種物理性質(zhì)的代碼組織。
邏輯層組織: 所謂邏輯層,主要是說,我們?nèi)绻褍蓚€不同功能的類或方法通過某種規(guī)范聯(lián)系和組織起來。 這里主要關(guān)注的是程序模塊間的接口。 這就是我們經(jīng)常見到的程序模塊的架構(gòu)。
單元測試是最接近BUG的地方,也是修改BUG成本最低的地方,同樣也是決定整個軟件質(zhì)量好壞的地方。所以,只要有可能,你就應(yīng)該寫更多的,更好的單元測試案例,這樣當(dāng)你未來有相應(yīng)代碼改變的時候,你可以很簡單的知道代碼的改變是否影響了其它單元。
軟件開發(fā)是一種持續(xù)的發(fā)現(xiàn)的過程,從而讓你的代碼可以跟上最新的實際需求的變化。 所以,我們要經(jīng)常重構(gòu)自己的代碼來跟上這樣的變化。 當(dāng)然,重構(gòu)是有風(fēng)險的,并不是所有的重構(gòu)都是成功的,也不是我們隨時都可以重構(gòu)代碼。
下面是兩個重構(gòu)代碼的先要條件,以避免讓你引入更多的BUG,或是把本來就爛的代碼變得更爛。
有大量的單元測試來測試。正如前面所說,重構(gòu)需要用大量的單元測試來做保障和測試。
每次重構(gòu)都不要大,用點點滴滴的小的重構(gòu)來代替那種大型的重構(gòu)。 有太多的時候,當(dāng)我們一開始計劃重構(gòu)2000行代碼,而在3個小時后, 我們就放棄這個計劃并把代碼恢復(fù)到原始的版本。 所以,我們推薦的是,重構(gòu)最好是從點點滴滴積累起來的。
這一條一定是充滿爭議的,大多數(shù)程序員都認(rèn)為程序注釋是非常好的, 是的,沒錯,程序注釋在理論上是非常不錯的。
但是,在實際過程中,程序員們寫出來的注釋卻是很糟糕的, 從而導(dǎo)致了程序注釋成為了一切邪惡的化身,也導(dǎo)致了我們在閱讀程序時, 大多數(shù)時候,我們都不讀注釋而直接讀代碼。
所以,在這里,我們并不是鼓勵不寫注釋,而是——如果你的注釋寫得不夠好的話, 那么,你還不如把更重要的時間花在重構(gòu)一下你的代碼,讓你的代碼更加易讀, 更加清楚,這會比注釋更好。
這是一個最經(jīng)典的規(guī)則了。
接口注重的是——“What”,是抽象,實現(xiàn)注重的是——“How”,是細(xì)節(jié)。 接口相當(dāng)于一種合同契約,而實際的細(xì)節(jié)相當(dāng)于對這種合同契約的一種運作和實現(xiàn)。 運作是可以很靈活的,而合同契約則需要是相對需要穩(wěn)定和不變的。
如果,一個接口沒有設(shè)計好,而需要經(jīng)常性的變化的話,那我們可以試想一下,這帶來的后果, 這絕對會是一件成本很大的事情。
所以,在軟件開發(fā)和調(diào)試中,接口是重中之重,而不是實現(xiàn)。 然而我們的程序員總是注重于實現(xiàn)細(xì)節(jié),所以他們局部的代碼寫的非常不錯, 但軟件整體卻設(shè)計得相對較差。 這點需要我們多多注意。
所有人都會出錯,一個人出錯的概率是很大的,兩個人出錯的概率就會小一些, 人多一些,出錯的概率就會越來越小。
因為,人多了,就能夠從不同的角度看待一個事情,雖然這樣可能導(dǎo)致無效率的爭論, 但比起軟件產(chǎn)品release后出現(xiàn)問題的維護(hù)成本,這點成本算是相當(dāng)值得的。
所以,這就是我們需要讓不同的人來review代碼,代碼審查機(jī)制不但是一種發(fā)現(xiàn)問題的最有效的機(jī)制, 同時也是一種可以知識共享的機(jī)制。
當(dāng)然,對于Code Review來說,下面有幾個基本原則:
審查者的能力一定要大于或等于代碼作者的能力, 不然,代碼審查就成了一種對新手的training。
而且,為了讓審查者真正負(fù)責(zé)起來,而不是在敷衍審查工作, 我們需要讓審查者對審查過的代碼負(fù)主要責(zé)任,而不是代碼的作者。
另外,好的代碼審查應(yīng)該不是當(dāng)代碼完成的時候, 而是在代碼編寫的過程中,不斷地迭代代碼審查。 好的實踐,無論代碼是否完成,代碼審核需要幾天一次地不斷地進(jìn)行。
原文:http://justjavac.com/other/2012/04/13/how-to-build-quality-code.html
更多建議: