不知道看什麼的時候看到了這篇文章,非常帶感,所以這裏寫一些自己的感受。>>圍觀原文請猛擊這裏<<
啥是Tell,Don't Ask?
在這裏,我把Alec Sharp大神的話摘抄在這裏:
Procedural code gets information then makes decisions. Object-oriented code tells objects to do things.
— Alec Sharp
我覺得這句話很好的將面向過程和麪向對象區分了出來。也就是說,對於對象來說,我應該直接告訴它你該做什麼,而不是先去問它的狀態,然後根據這個狀態再去告訴對象你需要做什麼什麼。
仔細回想一下,我還真在實際編程時做過這種事情:先去調用一個對象的getter方法得到某個狀態,然後判斷一下這個狀態,最後根據這個狀態調用對象的另外某個方法來讓對象去完成某個操作。其實,仔細想想,對象的狀態是什麼樣,該不該做,以及如何做都是對象自己該處理的,而不是我該操心的,這麼做只能破壞了對象的封裝,讓調用它的代碼和這個對象緊耦合起來。
那麼,這個原則真的很重要麼?
可以設想這樣一種情景:我在程序中調用一個對象的某個getter方法,得到一個該對象的內部狀態,比如得到的是一個String類型的東西。如果我接下來把這個String類型的東西用在了程序其它的地方,這時就產生了一種問題,我必須知道這個String類型的東西代表什麼意思。比如說這個String類型的值是”RED”,那麼,這個RED是什麼意思?是一種顏色?還是一個人名?還是某種縮寫?對於個東西的意思,那個對象再清楚不過了,而離開了那個對象,它就會被濫用和錯用。
我們常說,一個對象就是數據和方法的抽象與封裝。在我理解來看,就是使這寫狀態和方法有了上下文和語義,脫離了上下文,就破壞了它的封裝。
除了上面所說的問題,還有麼?
有!在編寫程序的時候,難免與Iterator打交道。舉個例子,比如有一組整數,我想在每個整數上加1,用Iterator的做法就是首先調用Iterator.hasNext()來判斷是否爲true,是的話調用Iterator.next()方法來取得一個整數,然後加1。但是這種做法在並行程序中就麻煩了。比如多線程程序,一個線程查詢Iterator.hasNext()爲true,但是另一個線程卻在它之前調用Iterator.next()方法將最後一個數據取走了,那麼前面的那個線程就杯具了。
仔細想想,其實我想做的僅僅是讓這組整數自加1,我只要告訴這個對象:每一個都加1,就可以了。至於如何加1的,不應該是我考慮的事情,而是對象自己的事情。
換成代碼應該是這樣的:
好吧,我把該讓對象做的,都移到對象內部去。
這樣沒問題了吧。我不在對象外邊去查詢它的狀態了。我讓對象內部自己去做。那麼,是不是在對象內部我可以隨便來查詢和調用對象的方法了?
不行,我們要遵守迪米特法則(Law of Demeter)
迪米特法則也稱爲最小知識原則(Least Knowledge Principle, LKP),簡單說,如果兩個對象不必直接通信,那麼這兩個對象就不應當發生直接的相互作用。如果一個對象需要調用另外一個對象的某個方法的話,那麼應該通過第三個對象來轉發調用。迪米特法則可以簡單的說成:Talk only to your immediate friends。
迪米特法則強調一個問題:如果我們在實現某個方法時調用的對象越多,那麼我們的程序耦合度就越高。一旦某個地方需要修改,都會帶來麻煩。所以根據迪米特法則,我們在實現對象O的方法M時,M能夠調用的對象應該只有:
- 對象O自己
- M的參數
- M內部創建的對象
- O的直接組件對象
舉個例子,我曾經這樣寫過某段代碼:
代碼很簡單,我只是單純的想往我們的商店中增加一本新的書。但是上面的這段簡單的代碼,依賴了bookStore、BookList甚至book對象,其實我們只是想單純的在書店中加一本新書而已,我什麼不能這麼用呢?
這樣,我們調用的代碼僅僅以來bookStore一個對象了。
所以,請記住Tell,Don't Ask
這個真的可以幫助我,讓我寫出更加抽象的、松耦合的代碼。