Java面向對象設計原則之迪米特法則介紹
一、迪米特法則的定義
迪米特法則,也稱為最少知識原則,雖然名字不同,但描述的是同一個規(guī)則:一個對象應該對其他對象有最少的了解。通俗地講,一個類應該對自己需要耦合或調用的類知道得最少,被耦合或調用的類的內部是如何復雜都和我沒關系,我就知道你提供的這么多public方法,我就調用這么多,其他的我一概不關心。
二、迪米特法則的含義
迪米特法則對類的低耦合提出了明確的規(guī)定,其包含以下幾層含義。
(一)、只和朋友交流
迪米特法則,要求只與直接朋友通信。什么叫直接朋友?每個對象都必然會與其他對象有耦合關系,兩個對象之間的耦合就成為朋友關系,這種關系的類型有很多,例如組合、聚合、依賴等。下面我們舉例說明如何才能做到只與直接朋友進行交流。
舉例:老師讓體育委員確認一下全班女生是否都到齊?類圖如下圖所示:
其實現過程如下代碼:
老師類:
public class Teacher { //老師發(fā)出命令,清點一下女生人數 public void command(GroupLeader groupLeader) { List<Girl> girls = new ArrayList<>(); for (int i = 0; i < 10; i++) { girls.add(new Girl()); } //告訴體育委員開始執(zhí)行清查任務 groupLeader.countGirls(girls); } }
老師只有一個方法command,先定義出所有的女生,然后發(fā)布命令給體育委員,去清點女生的人數。
體育委員GroupLeader的代碼如下:
public class GroupLeader { public void countGirls(List<Girl> girlList) { System.out.println("女生數量: " + girlList.size()); } }
老師類和體育委員類都對Girl類產生依賴,而且女生類不需要執(zhí)行任何動作,因此定義如下:
public class Girl { }
再定義一個場景類:
public class Client { public static void main(String[] args) { Teacher teacher = new Teacher(); teacher.command(new GroupLeader()); } }
運行結果如下:
女生數量: 10
體育委員按照老師的要求對女生進行了清點,并得出了數量。我們回過頭來思考一下這個程序有什么問題,首先確定Teacher類有幾個朋友類,它僅有一個朋友類-------GroupLeader,為什么Girl不是朋友類呢?Teacher對Girl類產生了依賴啊,朋友類的定義是這樣的:出現在成員變量、方法的輸入參數、輸出參數中的類成為成員朋友類。而Gril這個類是出現在command方法內部的,因此不屬于Teacher的直接朋友。
迪米特法則告訴我們一個類只與朋友類交流,但是我們剛剛定義的command方法卻與Girl類有了交流,聲明了List<Girl>動態(tài)集合,這樣就破壞了Teacher的健壯性。
問題已經發(fā)現,我們修改一下程序,將類圖稍作調整,如下圖所示:
修改后的老師類:
public class Teacher { //老師發(fā)出命令,清點一下女生人數 public void command(GroupLeader groupLeader) { //告訴體育委員開始執(zhí)行清查任務 groupLeader.countGirls(); } }
修改后的GroupLeader體育委員類:
public class GroupLeader { private List<Girl> girls; public GroupLeader(List<Girl> girls) { this.girls = girls; } public void countGirls() { System.out.println("女生數量: " + girls.size()); } }
在GroupLeader類中定義了一個構造函數,通過構造函數傳遞了依賴關系。同時,對場景類也進行了一些調整:
public class Client { public static void main(String[] args) { List<Girl> girls = new ArrayList<>(); //初始化女生信息 for (int i = 0; i < 10; i++) { girls.add(new Girl()); } ; Teacher teacher = new Teacher(); //老師發(fā)布命令 teacher.command(new GroupLeader(girls)); } }
對程序進行了簡單的修改,把Teacher的List<Girl>的初始化移動到了場景類中,同時在GroupLeader中增加對Girl的注入,避開了Teacher對陌生類Girl的訪問,降低了系統(tǒng)間耦合,提高了系統(tǒng)的健壯性。
(二)、朋友間也是有距離的
人和人之間是有距離的,太遠關系逐漸疏遠,最終形同陌路;太近就相互刺傷。迪米特法則就是對這個距離進行描述,即使是朋友類之間也不能無話不說,無所不知。
我們在安裝軟件的時候,經常會有一個導向動作,第一步是確認是否安裝,第二步確認Lisence,再然后選擇安裝目錄...這是一個典型的順序執(zhí)行動作,具體到程序中就是:調用一個或多個類,先執(zhí)行第一個方法,然后是第二個方法,根據返回結果再來看是否可以調用第三個方法,或者第四個方法,等等。其類圖大體如下:
實現過程如下:
public class Wizard { private Random random = new Random(System.currentTimeMillis()); /** * 第一步 * * @return */ public int first() { System.out.println("執(zhí)行第一個方法"); return random.nextInt(100); } /** * 第二步 * * @return */ public int second() { System.out.println("執(zhí)行第二個方法"); return random.nextInt(100); } /** * 第三步 * * @return */ public int third() { System.out.println("執(zhí)行第三個方法"); return random.nextInt(100); } }
在Wizard類中分別定義了三個步驟方法,每個步驟中都有相關的業(yè)務邏輯完成指定的任務,我們使用一個隨機函數來代替業(yè)務執(zhí)行的返回值。
InstallSoftware類的代碼如下:
public class InstallSoftware { public void install(Wizard wizard) { int first = wizard.first(); //根據first的返回結果,看是否需要執(zhí)行second if (first > 50) { int second = wizard.second(); if (second > 50) { int third = wizard.third(); if (third > 50) { wizard.first(); } } } } }
根據每個方法執(zhí)行的結果決定是否繼續(xù)執(zhí)行下一個方法,模擬人工的選擇操作。場景類如下:
public class Client { public static void main(String[] args) { InstallSoftware installSoftware = new InstallSoftware(); installSoftware.install(new Wizard()); } }
以上程序很簡單,運行結果和隨機數有關,每次執(zhí)行的結果都不相同,需要讀者自己運行并查看結果。程序雖然簡單,但隱藏的問題可不簡單,思考一下程序有什么問題?
Wizard類把太多的方法暴露給InstallSoftware類,兩者的朋友關系太親密了,耦合關系變得異常牢固。如果要將Wizard類中的first方法返回值的類型由int修改為boolean,就需要修改InstallSoftware類,從而把修改變更的風險擴散開了。因此,這種耦合是不合適的,我們需要對設計進行重構,重構后的類圖如下:
在Wizard類中增加一個installWizard方法,對安裝過程進行封裝,同時把所有的三個public方法修改為private方法,如下:
public class Wizard { private Random random = new Random(System.currentTimeMillis()); /** * 第一步 * * @return */ private int first() { System.out.println("執(zhí)行第一個方法"); return random.nextInt(100); } /** * 第二步 * * @return */ private int second() { System.out.println("執(zhí)行第二個方法"); return random.nextInt(100); } /** * 第三步 * * @return */ private int third() { System.out.println("執(zhí)行第三個方法"); return random.nextInt(100); } public void installWizard() { int first = this.first(); //根據first的返回結果,看是否需要執(zhí)行second if (first > 50) { int second = this.second(); if (second > 50) { int third = this.third(); if (third > 50) { this.first(); } } } } }
講啊三個步驟的訪問權限修改為private,同時把InstallSoftware中的方法installWizard()移動到了Wizard類中。通過這樣的重構后,Wizard類就只對外公布了一個public方法,即使要修改first方法的返回值,影響的也僅僅是Wizard本身,其他類不受影響,這顯示了類的高內聚特性。
修改后的InstallSoftware代碼如下:
public class InstallSoftware { public void install(Wizard wizard) { wizard.installWizard(); } }
場景類沒有任何改變,通過進行重構,類間的耦合關系變弱了,結構也清晰了,變更引起的風險也變小了。
一個類公開的public屬性或者方法越多,修改時涉及的面就越大,變更引起的風險擴散也就越大。因此,為了保持朋友間的距離,在設計時需要反復衡量:是否還可以再減少public屬性和方法,是否可以修改為private、package-private、protected等訪問權限,是否可以加上final關鍵字等。
(三)、是自己的就是自己的
在實際應用中經常會出現這樣一個方法:放在本類中也可以,放在其他類也沒有錯,那怎么去衡量呢?可以檢查這樣一個原則:如果一個方法放在本類中,既不增加類間關系,也對本類不產生負面影響,那就放置在本類中。
三、總結
迪米特法則的核心觀念就是類間解耦,弱耦合,只有弱耦合了以后,類的復用率才可以提高。其要求的結果就是產生了大量的中轉或跳轉類,導致系統(tǒng)的復雜性提高,同時也為維護帶來了難度。讀者在采用迪米特法則的時候需要反復權衡,既做到讓結構清晰,又做到高內聚低耦合。
到此這篇關于Java面向對象設計原則之迪米特法則介紹的文章就介紹到這了,更多相關Java迪米特法則內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
原因分析IDEA導入Spring-kafka項目Gradle編譯失敗
這篇文章主要為大家介紹分析了IDEA導入Spring-kafka項目Gradle中編譯失敗原因及解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2022-02-02springcloud 中 zuul 修改請求參數信息的方法
這篇文章主要介紹了springcloud 中 zuul 修改請求參數信息的方法,需要的朋友可以參考下2018-02-02