王垠:DRY原則的誤區

>>>  技術話題—商業文明的嶄新時代  >>> 簡體     傳統

  很多編程的人,喜歡鼓吹各種各樣的原則,比如 KISS 原則,DRY 原則 總有人把這些所謂原則奉為教條或者秘方,以為兢兢業業地遵循這些,空喊幾個口號,就可以寫出好的代碼。同時,他們對違反這些原則的人嗤之以鼻你不知道,不遵循或者藐視這些原則,那么你就是菜鳥。所謂DRY 原則(Don't Repeat Yourself,不要重復你自己)就是這些教條其中之一。盲目的迷信 DRY 原則,在實際的工程中帶來了各種各樣的問題,卻經常被忽視。

  簡言之,DRY 原則鼓勵對代碼進行抽象,但是鼓勵得過了頭。DRY 原則說,如果你發現重復的代碼,就把它們提取出去做成一個模板或者框架。對于抽象我非常的在行,實際上程序語言專家做的許多研究,就是如何設計更好的抽象。然而我并不奉行所謂 DRY 原則,并不是盡一切可能避免重復。避免重復并不等于抽象。有時候適當的重復代碼是有好處的,所以我有時候會故意的進行重復。

  抽象與可讀性的矛盾

  代碼的抽象和它的可讀性(直觀性),其實是一對矛盾的關系。適度的抽象和避免重復是有好處的,它甚至可以提高代碼的可讀性,然而如果你盡一切可能從代碼里提取模板,甚至把一些微不足道的共同點也提出來進行共享,它就開始有害了。這是因為,模板并不直接顯示在調用它們的位置。提取出模板,往往會使得閱讀代碼時不能一目了然。如果由此帶來的直觀性損失超過了模板所帶來的好處時,你就應該考慮避免抽象了。要知道,代碼讀的次數要比寫的次數多很多。很多人為了一時的寫的快感,過早的提取出不必要的模板,其實損失了讀代碼時的直觀性。如果自己的代碼連自己都不能一目了然,你就不能寫出優雅的代碼。

  舉一個實際的例子。奉行 DRY 原則的人,往往喜歡提取類里面的共同 field,把它們放進一個父類,然后讓原來的類繼承這個父類。比如,本來的代碼可能是:

class A {  int a;  int x;  int y;
}class B {  int a;  int u;  int v;
}

  奉行 DRY 原則的人喜歡把它改成這樣:

class C {  int a;
}class A extends C {  int x;  int y;
}class B extends C {  int u;  int v;
}

  后面這段代碼有什么害處呢?它的問題是,當你看到class Aclass B的定義時,你不再能一目了然的看到int a這個 field。可見性,對于程序員能夠產生直覺,是非常重要的。這種無關緊要的 field,其實大部分時候都沒必要提出去,造出一個新的父類。很多時候,不同類里面雖然有同樣的int a這樣的 field,然而它們的含義卻是完全不同的。有些人不管三七二十一就來個DRY,結果不但沒帶來好處,反而讓程序難以理解。

  抽象的時機問題

  奉行 DRY 原則的人還有一個問題,就是他們隨時都在試圖發現將來可能重用的代碼,而不是等到真的出現重復的時候再去做抽象。很多時候他們提取出一個貌似經典模板,結果最后過了幾個月發現,這個模板在所有代碼里其實只用過一次。這就是因為他們過早的進行了抽象。

  抽象的思想,關鍵在于發現兩個東西是一樣的。然而很多時候,你開頭覺得兩個東西是一回事,結果最后發現,它們其實只是膚淺的相似,而本質完全不同。同一個int a,其實可以表示很多種風馬牛不及的性質。你看到都是int a就提出來做個父類,其實反而讓程序的概念變得混亂。還有的時候,有些東西開頭貌似同類,后來你增添了新的邏輯之后,發現它們的用途開始特殊化,后來就分道揚鑣了。過早的提取模板,反而捆住了你的手腳,使得你為了所謂一致性而重復一些沒用的東西。這樣的一致性,其實還不如針對每種情況分別做特殊處理。

  防止過早抽象的方法其實很簡單,它的名字叫做等待。其實就算你不重用代碼,真的不會死人的。時間能夠告訴你一切。如果你發現自己仿佛正在重復以前寫過代碼,請先不要停下來,請堅持把這段重復的代碼寫完。如果你不把它寫出來,你是不可能準確的發現重復的代碼的,因為它們很有可能到最后其實是不一樣的。

  你還應該避免沒有實際效果的抽象。如果代碼才重復了兩次,你就開始提取模板,也許到最后你會發現,這個模板總共也就只用了兩次!只重復了兩次的代碼,大部分時候是不值得為它提取模板的。因為模板本身也是代碼,而且抽象思考本身是需要一定代價的。所以最后總的開銷,也許還不如就讓那兩段重復的代碼待在里面。

  這就是為什么我喜歡一種懶懶的,笨笨的感覺。因為我懶,所以我不會過早的思考代碼的重用。我會等到事實證明重用一定會帶來好處的時候,才會開始提取模板,進行抽象。經驗告訴我,每一次積極地尋找抽象,最后的結果都是制造一些不必要的模板,搞得自己的代碼自己都看不懂。很多人過度強調 DRY,強調代碼的重用,隨時隨地想著抽象,結果被這些抽象攪混了頭腦,bug 百出,寸步難行。如果你不能寫出可用(usable)的代碼,又何談可重用(reusable)的代碼呢?

  謹慎的對待所謂原則

  說了這么多,我是在支持 DRY,還是反對 DRY 呢?其實不管是支持還是反對它,都會表示我在乎它,而其實呢,我完全不在乎這類原則,因為它們非常的膚淺。這就像你告訴我說你有一個重大的發現,那就是1+1=2,我該支持你還是反對你呢?我才懶得跟你說話。人們寫程序,本來自然而然就會在合適的時候進行抽象,避免重復,怎么過了幾十年后,某個菜鳥給我們的做法起了個名字叫 DRY,反而他成了大師一樣的人物,我倒要用DRY這個詞來描述我一直在干的事情呢?所以我根本不愿意提起DRY這個名字。

  所以我覺得這個 DRY 原則根本就不應該存在,它是一個根本沒有資格提出原則的人提出來的。看看他鼓吹的其它低劣東西(比如 Agile,Ruby),你就會發現,他是一個兜售減肥藥的軟件工程專家。世界上有太多這樣的膚淺的所謂原則,我不想對它們一一進行評價,這是在浪費我的時間。世界上有比這些喜歡提出原則的軟件工程專家深邃很多的人,他們懂得真正根本的原理。


Cnblogs www.yinwang.org 2015-08-23 08:57:44

[新一篇] 愛能被量化嗎 - 《星際穿越》的兩個終極問題

[舊一篇] 王垠:數學和編程
回頂部
寫評論


評論集


暫無評論。

稱謂:

内容:

驗證:


返回列表