也稱為最少知道原則(Least Knowledge Principle 簡寫LKP)
迪米特法則是用來干什么的?
他的初衷是:降低類之間的耦合。
以我目前的視野,我認為迪米特法則就是一個在類創建方法和屬性時需要遵守的法則。
什么是迪米特法則?
迪米特法則由三個守則組成:
守則1:是自己的就是自己的
如果一個方法放在本類中,即不增加類間關系,也對本類不產生負面影響就放在本類中。
守則2:一個類只與他的朋友類進行交互。talk only to your immediate friends
在弄明白這個守則之前,我們需要弄清什么叫做朋友類:
在遵守守則1的前提下,為類A創建了一個方法,那么該方法Method中的參數必然是某基本類型(包括void類型)的變量,或者是某個類的對象。
如果是后者即某個類的對象,我們將該類稱之為類B,那么類B即為類A的朋友類。
那么守則2的意思是類A的方法Method只與類B以及類A的屬性進行交流,在該方法中不再引入其他第三方類。
除了方法中的參數的類型可能成為朋友類,屬性的類型也是朋友類(基本類型除外)。
以下給出例子:
例如,老是想讓體育委員確認以下女生人數,我們來看怎么用程序來實現,類圖如圖:
Teacher類的commond方法負責發送命令給體育委員,命令他清點女生,其實現過程如下:
老師只有一個commond方法,先定義出所有的女生,然后發命令給體育委員,去清點以下女生的數量。體育委員GroupLeader的實現過程如下:
老師類和體育委員類都對女生類產生了依賴,而且女生類不需要執行任何動作,因此定義一個空類,如圖:
故事中的三個角色都有了,在定義一個場景來描述這個故事,實現過程如下:
運行結果:
體育委員按照老師的要求對女生進行了清點,並得出了數量。我們回過頭來思考一下這個程序有什么問題,首先確定Teacher有幾個朋友類,他僅有一個朋友類:GroupLeader。為什么Girl不是朋友類呢?Teacher也對他產生了依賴關系啊!朋友類的定義是這樣的:出現在成員變量,方法的輸入輸出中的類稱為成員朋友類。迪米特法則告訴我們一個類只和朋友類交流,但是我們剛剛定義的commond方法卻與Girl類有了交流,聲明了一個List動態數組,也就是與一個陌生的類Girl有了交流,這樣就破壞了Teacher的健壯性。方法是類的一個行為,類竟然不知道與其他類產生了依賴關系,這是不允許的,嚴重違反了迪米特法則。
可以這樣子修改,在類圖中去掉Teacher對Girl類的依賴關系,修改后的Teacher類如下:
修改后的體育委員類:
修改后的場景類:
把Teacher中對List的初始化移動到了場景類中,同時在GroupLeader中增加了對Girl的注入,避開了Teacher類對陌生類Girl的訪問,降低了系統間的耦合,提高了系統的健壯性。
注意:一個類只和朋友交流,不與陌生類交流,類與類之間的關系是建立在類間的,而不是方法間,因此一個方法盡量不引入一個類中不存在的對象,當然JDK API提供的類除外。
守則3:朋友間也是有距離的
朋友間是有距離的,太遠關系逐漸疏遠,最終形成陌路;太近就互相刺傷,必須保持一個合適的距離。迪米特法則就是對這個距離的描述,即使是朋友類之間也不能無話不說,無所不知。
例如,我們在安裝軟時,經常有一個導向動作,第一步是確認是否安裝,第二步是確認License,然后再選擇安裝目錄;這是一個典型的順序執行動作,具體到程序中就是:調用一個或者多個類,先執行第一個方法,然后是第二個方法,根據返回結果再來看是否調用第三個方法,其類圖如下:
很簡單的類圖,實現軟件安裝的過程,其中first方法定義第一步做什么,second方法定義第二步做什么,其實現過程如下:
程序雖然簡單,隱藏的問題可不簡單,思考一下程序有什么問題。Wizard類把太多的方法暴露給InstallSoftware類,兩者的朋友關系太緊密了,耦合關系變得異常牢固。如果要將Wizard類中的first方法返回值的類型由int改為boolean,就需要修改InstallSoftware類,從而把修改變更的風險擴散開來。因此這樣的耦合是極度不合適的,我們需要重新對設計進行重構,重構后的類圖如下:
在Wizard類方法中增加一個installWizard方法,對安裝過程進行封裝,同時把原有的三個public方法修改為private方法,如下所示:
修改后的InstallSoftware類:
通過這樣的重構后,Wizard類對外只公布了一個public方法,即使要修改first的返回值,影響的也只是Wizard本身,其他類不受影響,這顯示了類的高內聚特性。
注意:迪米特法則要求類“羞澀”一點,盡量不要對外公布太多的public方法和非靜態的public變量,盡量內斂,多使用private、protected等訪問權限。