王垠: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;
} </code></pre>
奉行DRY原則的人喜歡把它改成這樣:
class C {
int a;
}
class A extends C {
int x;
int y;
}
class B extends C {
int u;
int v;
} </code></pre>
后面這段代碼有什么害處呢?它的問題是,當你看到class A
和class 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),你就會發現,他是一個兜售減肥藥的“軟件工程專家”。世界上有太多這樣的膚淺的所謂原則,我不想對它們一一進行評價,這是在浪費
我的時間。世界上有比這些喜歡提出“原則”的軟件工程專家深邃很多的人,他們懂得真正根本的原理。
來自: www.yinwang.org
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!