整潔又好看的代碼是什么?我們又該如何實(shí)現(xiàn)?
譯文【51CTO.com快譯】最近,我們公司內(nèi)部一直在討論如何寫(xiě)出整潔的代碼的相關(guān)研討會(huì),他們認(rèn)為一份整潔的代碼能為團(tuán)隊(duì)開(kāi)發(fā),后期維護(hù),重構(gòu)奠定了良好的基礎(chǔ),其質(zhì)量也是可靠的。因此各小組以如何建立并監(jiān)督編碼標(biāo)準(zhǔn)展開(kāi)了大量的討論。雖然我同意這類(lèi)作法確實(shí)有一定的作用,但我認(rèn)為整潔代碼最核心的關(guān)鍵并不是這個(gè)。因此,以下內(nèi)容是我個(gè)人對(duì)整潔代碼的理解與看法。
(免責(zé)聲明:以下內(nèi)容并非我個(gè)人原創(chuàng)。這一切都來(lái)自眾多偉大程序員的指導(dǎo)思想。但我認(rèn)為,重要內(nèi)容值得一再?gòu)?qiáng)調(diào)。)
首先,我個(gè)人認(rèn)為整潔代碼這一說(shuō)法并不準(zhǔn)確。這一術(shù)語(yǔ)很容易誤導(dǎo)大家以為僅僅是讓代碼看起來(lái)更加整潔就可以了,而這顯然是很膚淺的表面現(xiàn)象。相反,我認(rèn)為將它稱為“宜居型代碼”更為合適,正如Richard Gabriel所說(shuō):
宜居型代碼是源代碼的一種特性,即允許程序員、編碼者、bug修復(fù)者以及其他人在***閱讀代碼的人能很清晰的明白這里在干什么,而不是隱澀難懂,并能很快的加以調(diào)整。
宜居型代碼會(huì)讓人有種賓至如歸的體驗(yàn),只有這樣,開(kāi)發(fā)者才能在無(wú)需深入考量的前提下快速著手工作。
雖然良好的代碼格式,會(huì)使得我們閱讀更容易且查找更快速,但其本質(zhì)的宜居型是關(guān)鍵。在我看來(lái),宜居型代碼是指代碼能隨著業(yè)務(wù)的需求任意改變的。因此,在小規(guī)模業(yè)務(wù)要求調(diào)整代碼時(shí)只需要投入低成本的代價(jià)就可實(shí)現(xiàn),而當(dāng)大規(guī)模業(yè)務(wù)要求調(diào)整代碼時(shí)只需要再投入較多的開(kāi)發(fā)成本即可實(shí)現(xiàn)——因?yàn)樾滦枨笈c現(xiàn)有代碼庫(kù)不相契合,所以無(wú)法共享原有的代碼。
考慮到這一定義,我認(rèn)為宜居型代碼的關(guān)鍵在于整體結(jié)構(gòu)。而只有頂層設(shè)計(jì)(架構(gòu))能夠?qū)⒁司有偷奶匦宰優(yōu)楝F(xiàn)實(shí)。而***實(shí)現(xiàn)途徑包括以下幾項(xiàng):
- 應(yīng)用程序要被劃分為多個(gè)(不應(yīng)過(guò)多)模塊。
- 每個(gè)模塊代表著其領(lǐng)域里的一個(gè)特定意義, 其命名要一目了然,且不存在歧義,并要確保***閱讀代碼的人能快速了解其作用。
- 各模塊要擁有一個(gè)經(jīng)過(guò)良好定義的接口,同樣要有一個(gè)特定且唯一的名字來(lái)命名。
- 每個(gè)模塊的生命周期和各個(gè)模塊之間的關(guān)系是以聲明方式在應(yīng)用程序的入口點(diǎn)中表示。具體來(lái)講,應(yīng)用入口點(diǎn)必須明確聲明這些模塊是以何種方式來(lái)對(duì)接的,并要提供必要的業(yè)務(wù)價(jià)值。
- 各模塊間的共生性要在各層級(jí)代碼得到明確表達(dá)。
(這部分內(nèi)容其實(shí)就是對(duì)結(jié)構(gòu)化程序設(shè)計(jì)方法的再次重申。正如之前提到,我并沒(méi)有創(chuàng)造任何新鮮概念。)
在我看來(lái),這些模塊屬于形式與功能的組合——命名是區(qū)分應(yīng)用區(qū),而行為是描述業(yè)務(wù)。不同模塊間的關(guān)系應(yīng)當(dāng)?shù)玫铰暶?,且不存在任何隱藏或任何歧義,直接了當(dāng)表明關(guān)系。
我還認(rèn)為,這一定義同樣適用于遞歸。著眼于任何***模塊,其中每個(gè)模塊都應(yīng)與整體應(yīng)用顯示出同樣的特性:
- 拆分為多個(gè)(不能太多)子模塊。
- 每個(gè)子模塊要擁有一個(gè)經(jīng)過(guò)良好定義的接口。
- 各子模塊的關(guān)系與生命周期應(yīng)在該模塊的入口點(diǎn)中聲明。
- 各子模塊間的共生性應(yīng)在代碼中得到明確表達(dá)。
這些嵌套模塊可以作為應(yīng)用、微服務(wù)、軟件包、命名空間、綁定上下文、聚合、模塊、對(duì)象乃至功能形式存在——這一切皆可被視為“封裝單元”或者“對(duì)象”。
-
這一原則與Page-Jones重構(gòu)算法能夠相互印證:
-
將應(yīng)用劃分為多個(gè)(不能太多)模塊。
-
移除各模塊間的全部高階共生性。
-
遞歸直至無(wú)意義。
任何模塊的拆分其子模塊都能代表著另一組實(shí)現(xiàn)。并且我希望能夠?qū)⒏鱾€(gè)模塊作為一個(gè)黑匣子看待,且能在無(wú)沖突前提下對(duì)黑匣子邊界進(jìn)行重構(gòu)。這需要該模塊的接口有個(gè)一良好定義(例如通過(guò)自動(dòng)化測(cè)試實(shí)現(xiàn)),且各模塊間及子模塊間的共生性明確且可理解。
在我看來(lái),隨著層級(jí)降低,利用這一算法的價(jià)值也將隨之降低。換言之,明確頂層模塊的關(guān)系,其價(jià)值要高于明確底層子模塊間的關(guān)系。這樣的應(yīng)用更易于變更,特別是考慮到頂層代碼要采取簡(jiǎn)單的表達(dá)方式。雖然整潔的低級(jí)別模塊同樣能帶來(lái)好處,但實(shí)現(xiàn)應(yīng)用宜居型的整體收益相對(duì)較低。
作為實(shí)現(xiàn)手段之一,我認(rèn)為面向消息型編程能夠在各類(lèi)規(guī)模下能達(dá)到良好的收效。我之所以將應(yīng)用劃分成多個(gè)模塊,單純是為了讓各模塊間的通信變得簡(jiǎn)單且意義明確。模塊的存在只是為了隱藏消息以及層的變更是如何實(shí)際“生效”的。Michael Feathers提出的Naked CRC技術(shù)同樣能夠很好地實(shí)現(xiàn)宜局型模塊的通信。
遺憾的是,我所見(jiàn)過(guò)的大部分代碼庫(kù)都粗暴地違反了以上各種實(shí)現(xiàn)模式。其通常包含著數(shù)百個(gè)毫無(wú)結(jié)構(gòu)性可言的類(lèi),這些類(lèi)全部存在于同一抽象及可訪問(wèn)層內(nèi)。這樣的代碼庫(kù)絕無(wú)宜人性可言,且很難向其中添加新的類(lèi)或者概括其中現(xiàn)有類(lèi)的作用。這類(lèi)應(yīng)用同樣難于重構(gòu),因?yàn)榇蠖鄶?shù)“單元測(cè)試”機(jī)制著眼于實(shí)現(xiàn)選擇的而非業(yè)務(wù)要求。而且由于不具備頂層封裝的單元,因此我們很難理解其整體設(shè)計(jì)思路。
原文標(biāo)題:What Is Clean Code?
原文作者:Kevin Rutherford
【51CTO譯稿,合作站點(diǎn)轉(zhuǎn)載請(qǐng)注明原文譯者和出處為51CTO.com】