這里我說了算!
自從9歲那年得到第一臺Commodore 64家用電腦起,我就開始編程。然而,當面對如何寫出好的代碼時,我仍然感覺自己還有很多要學的。
在探索如何提高自己的過程中,我學了很多種語言。大多數是以面向對象為主的(OO)。
然而,讓我驚訝的是,在我讀過的大多數書本、雜志和網上文章中,有著大量遭透了的被當作面向對象例子的代碼。
這些代碼中,我看到的最多被違反的原則是“命令,不要去詢問(Tell, Don’t Ask)”原則。這個原則講的是,一個對象應該命令其它對象該做什么,而不是去查詢其它對象的狀態來決定做什么(查詢其它對象的狀態來決定做什么也被稱作 ‘功能嫉妒(Feature Envy)’)。在面向對象的編程中,一個對象被定義成由對象狀態和操作這個狀態的方法組成。
在《Holub on Patterns: Learning Design Patterns By Looking At Code》這本書里,Allen Holub在第一章里有一節的標題是“為什么getter和setter方法有害”。他在JavaWorld上的一篇文章里也談論了這個問題。對所有的面向對象的程序員來說,這應該是一篇“必讀”文章。
我有一些程序員同事,他們在一個對象上第一步聲明了屬性后,第二步就是添加getter和setter方法。JavaBean規范對于這種文化的推廣負于很大的責任。人們認為這是一種能讓你寫出可復用的模塊化組建的好方法,但這已是很多年前的事了,時過境遷。
寫帶有getter和setter方法的類會導致過程式的代碼。通過getter和setter來獲取數據進行操作的邏輯最終會遍布整個應用,進而 經常導致應用內的重復(這違反了另外一個原則:DRY——不要自我重復(Don’t Repeat Yourself))。這會致使產生很難維護的代碼,當你對一個類做任何修改時,都會在整個應用內造成連鎖式的牽連。
用這種方式來暴露數據還會妨礙你重構你的類,因為對這樣的屬性的任何修改都意味著會影響到訪問了這個屬性的其它類。
違反“命令,不要去詢問”原則的另外一個副作用是,你的探詢最終變成嚴重依賴狀態信息并帶有很多前提條件。這會讓人很難理解你究竟詢問的是什么。
你很可能會最終違反的第三個原則是,盡少知道(Least Knowledge)原則,也叫做得墨忒耳定律(Law Of Demeter)。這個定律可以總結為下面一句好:
一個類應該只跟它的直接朋友通話,不要跟陌生人說話。
在類里面加入getter方法,你的代碼最終會寫成這樣:
1 |
if (person.getAddress().getCountry() == "Australia" ) { |
這違反了得墨忒耳定律,因為這個調用者跟Person過于親密。它知道Person里有一個Address,而Address里還有一個country。它實際上應該寫成這樣:
1 |
if (person.livesIn( "Australia" )) { |
這并不違反得墨忒耳定律,因為這個調用者只跟它的直接朋友Person通話,而且它不知道內部情況。從“命令,不要詢問”的視角來看,這也是更好, 因為確定這個person是否居住在Australia的邏輯被隱藏到了Person里,如果我們改變Person里存儲國家信息的方式,這將不會影響任 何依賴這個信息的其它類。
另外一個這樣寫代碼的好處是,它把我們的意圖顯示的很清楚。這是我會在以后的時間里討論的另外一個話題。
為了避免違反“命令,不要去詢問”原則,有幾樣事情你要記在心里。
- 在IDE里敲擊快捷生成鍵前,詢問自己’五個為什么’,問問自己為什么一上來就添加getter和setter方法。
- 不要詢問對象的狀態。要做什么,告訴它們該怎么做。
- 操作所屬的對象擁有這些操作的數據。
進一步閱讀
我的一個朋友是這個思想的大力倡導者,他用East Oriented方式精彩的解釋了這個原則。
《Tell Don’t Ask and the effect on testing》,出自Steve Freeman 和 Nat Pryce,他們也是 《Growing Object-Oriented Software》, 《Guided by Tests》 兩本書的作者。
[本文英文原文鏈接:I give the orders around here! ]
本文轉載自: 外刊IT評論 http://www.aqee.net/