Java面向?qū)ο笤O(shè)計(jì)原則之迪米特法則介紹
一、迪米特法則的定義
迪米特法則,也稱為最少知識(shí)原則,雖然名字不同,但描述的是同一個(gè)規(guī)則:一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象有最少的了解。通俗地講,一個(gè)類應(yīng)該對(duì)自己需要耦合或調(diào)用的類知道得最少,被耦合或調(diào)用的類的內(nèi)部是如何復(fù)雜都和我沒(méi)關(guān)系,我就知道你提供的這么多public方法,我就調(diào)用這么多,其他的我一概不關(guān)心。
二、迪米特法則的含義
迪米特法則對(duì)類的低耦合提出了明確的規(guī)定,其包含以下幾層含義。
(一)、只和朋友交流
迪米特法則,要求只與直接朋友通信。什么叫直接朋友?每個(gè)對(duì)象都必然會(huì)與其他對(duì)象有耦合關(guān)系,兩個(gè)對(duì)象之間的耦合就成為朋友關(guān)系,這種關(guān)系的類型有很多,例如組合、聚合、依賴等。下面我們舉例說(shuō)明如何才能做到只與直接朋友進(jìn)行交流。
舉例:老師讓體育委員確認(rèn)一下全班女生是否都到齊?類圖如下圖所示:

其實(shí)現(xiàn)過(guò)程如下代碼:
老師類:
public class Teacher {
//老師發(fā)出命令,清點(diǎn)一下女生人數(shù)
public void command(GroupLeader groupLeader) {
List<Girl> girls = new ArrayList<>();
for (int i = 0; i < 10; i++) {
girls.add(new Girl());
}
//告訴體育委員開(kāi)始執(zhí)行清查任務(wù)
groupLeader.countGirls(girls);
}
}老師只有一個(gè)方法command,先定義出所有的女生,然后發(fā)布命令給體育委員,去清點(diǎn)女生的人數(shù)。
體育委員GroupLeader的代碼如下:
public class GroupLeader {
public void countGirls(List<Girl> girlList) {
System.out.println("女生數(shù)量: " + girlList.size());
}
}老師類和體育委員類都對(duì)Girl類產(chǎn)生依賴,而且女生類不需要執(zhí)行任何動(dòng)作,因此定義如下:
public class Girl {
}再定義一個(gè)場(chǎng)景類:
public class Client {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.command(new GroupLeader());
}
}運(yùn)行結(jié)果如下:
女生數(shù)量: 10
體育委員按照老師的要求對(duì)女生進(jìn)行了清點(diǎn),并得出了數(shù)量。我們回過(guò)頭來(lái)思考一下這個(gè)程序有什么問(wèn)題,首先確定Teacher類有幾個(gè)朋友類,它僅有一個(gè)朋友類-------GroupLeader,為什么Girl不是朋友類呢?Teacher對(duì)Girl類產(chǎn)生了依賴啊,朋友類的定義是這樣的:出現(xiàn)在成員變量、方法的輸入?yún)?shù)、輸出參數(shù)中的類成為成員朋友類。而Gril這個(gè)類是出現(xiàn)在command方法內(nèi)部的,因此不屬于Teacher的直接朋友。
迪米特法則告訴我們一個(gè)類只與朋友類交流,但是我們剛剛定義的command方法卻與Girl類有了交流,聲明了List<Girl>動(dòng)態(tài)集合,這樣就破壞了Teacher的健壯性。
問(wèn)題已經(jīng)發(fā)現(xiàn),我們修改一下程序,將類圖稍作調(diào)整,如下圖所示:

修改后的老師類:
public class Teacher {
//老師發(fā)出命令,清點(diǎn)一下女生人數(shù)
public void command(GroupLeader groupLeader) {
//告訴體育委員開(kāi)始執(zhí)行清查任務(wù)
groupLeader.countGirls();
}
}修改后的GroupLeader體育委員類:
public class GroupLeader {
private List<Girl> girls;
public GroupLeader(List<Girl> girls) {
this.girls = girls;
}
public void countGirls() {
System.out.println("女生數(shù)量: " + girls.size());
}
}在GroupLeader類中定義了一個(gè)構(gòu)造函數(shù),通過(guò)構(gòu)造函數(shù)傳遞了依賴關(guān)系。同時(shí),對(duì)場(chǎng)景類也進(jìn)行了一些調(diào)整:
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));
}
}對(duì)程序進(jìn)行了簡(jiǎn)單的修改,把Teacher的List<Girl>的初始化移動(dòng)到了場(chǎng)景類中,同時(shí)在GroupLeader中增加對(duì)Girl的注入,避開(kāi)了Teacher對(duì)陌生類Girl的訪問(wèn),降低了系統(tǒng)間耦合,提高了系統(tǒng)的健壯性。
(二)、朋友間也是有距離的
人和人之間是有距離的,太遠(yuǎn)關(guān)系逐漸疏遠(yuǎn),最終形同陌路;太近就相互刺傷。迪米特法則就是對(duì)這個(gè)距離進(jìn)行描述,即使是朋友類之間也不能無(wú)話不說(shuō),無(wú)所不知。
我們?cè)诎惭b軟件的時(shí)候,經(jīng)常會(huì)有一個(gè)導(dǎo)向動(dòng)作,第一步是確認(rèn)是否安裝,第二步確認(rèn)Lisence,再然后選擇安裝目錄...這是一個(gè)典型的順序執(zhí)行動(dòng)作,具體到程序中就是:調(diào)用一個(gè)或多個(gè)類,先執(zhí)行第一個(gè)方法,然后是第二個(gè)方法,根據(jù)返回結(jié)果再來(lái)看是否可以調(diào)用第三個(gè)方法,或者第四個(gè)方法,等等。其類圖大體如下:

實(shí)現(xiàn)過(guò)程如下:
public class Wizard {
private Random random = new Random(System.currentTimeMillis());
/**
* 第一步
*
* @return
*/
public int first() {
System.out.println("執(zhí)行第一個(gè)方法");
return random.nextInt(100);
}
/**
* 第二步
*
* @return
*/
public int second() {
System.out.println("執(zhí)行第二個(gè)方法");
return random.nextInt(100);
}
/**
* 第三步
*
* @return
*/
public int third() {
System.out.println("執(zhí)行第三個(gè)方法");
return random.nextInt(100);
}
}在Wizard類中分別定義了三個(gè)步驟方法,每個(gè)步驟中都有相關(guān)的業(yè)務(wù)邏輯完成指定的任務(wù),我們使用一個(gè)隨機(jī)函數(shù)來(lái)代替業(yè)務(wù)執(zhí)行的返回值。
InstallSoftware類的代碼如下:
public class InstallSoftware {
public void install(Wizard wizard) {
int first = wizard.first();
//根據(jù)first的返回結(jié)果,看是否需要執(zhí)行second
if (first > 50) {
int second = wizard.second();
if (second > 50) {
int third = wizard.third();
if (third > 50) {
wizard.first();
}
}
}
}
}根據(jù)每個(gè)方法執(zhí)行的結(jié)果決定是否繼續(xù)執(zhí)行下一個(gè)方法,模擬人工的選擇操作。場(chǎng)景類如下:
public class Client {
public static void main(String[] args) {
InstallSoftware installSoftware = new InstallSoftware();
installSoftware.install(new Wizard());
}
}以上程序很簡(jiǎn)單,運(yùn)行結(jié)果和隨機(jī)數(shù)有關(guān),每次執(zhí)行的結(jié)果都不相同,需要讀者自己運(yùn)行并查看結(jié)果。程序雖然簡(jiǎn)單,但隱藏的問(wèn)題可不簡(jiǎn)單,思考一下程序有什么問(wèn)題?
Wizard類把太多的方法暴露給InstallSoftware類,兩者的朋友關(guān)系太親密了,耦合關(guān)系變得異常牢固。如果要將Wizard類中的first方法返回值的類型由int修改為boolean,就需要修改InstallSoftware類,從而把修改變更的風(fēng)險(xiǎn)擴(kuò)散開(kāi)了。因此,這種耦合是不合適的,我們需要對(duì)設(shè)計(jì)進(jìn)行重構(gòu),重構(gòu)后的類圖如下:

在Wizard類中增加一個(gè)installWizard方法,對(duì)安裝過(guò)程進(jìn)行封裝,同時(shí)把所有的三個(gè)public方法修改為private方法,如下:
public class Wizard {
private Random random = new Random(System.currentTimeMillis());
/**
* 第一步
*
* @return
*/
private int first() {
System.out.println("執(zhí)行第一個(gè)方法");
return random.nextInt(100);
}
/**
* 第二步
*
* @return
*/
private int second() {
System.out.println("執(zhí)行第二個(gè)方法");
return random.nextInt(100);
}
/**
* 第三步
*
* @return
*/
private int third() {
System.out.println("執(zhí)行第三個(gè)方法");
return random.nextInt(100);
}
public void installWizard() {
int first = this.first();
//根據(jù)first的返回結(jié)果,看是否需要執(zhí)行second
if (first > 50) {
int second = this.second();
if (second > 50) {
int third = this.third();
if (third > 50) {
this.first();
}
}
}
}
}講啊三個(gè)步驟的訪問(wèn)權(quán)限修改為private,同時(shí)把InstallSoftware中的方法installWizard()移動(dòng)到了Wizard類中。通過(guò)這樣的重構(gòu)后,Wizard類就只對(duì)外公布了一個(gè)public方法,即使要修改first方法的返回值,影響的也僅僅是Wizard本身,其他類不受影響,這顯示了類的高內(nèi)聚特性。
修改后的InstallSoftware代碼如下:
public class InstallSoftware {
public void install(Wizard wizard) {
wizard.installWizard();
}
}場(chǎng)景類沒(méi)有任何改變,通過(guò)進(jìn)行重構(gòu),類間的耦合關(guān)系變?nèi)趿?,結(jié)構(gòu)也清晰了,變更引起的風(fēng)險(xiǎn)也變小了。
一個(gè)類公開(kāi)的public屬性或者方法越多,修改時(shí)涉及的面就越大,變更引起的風(fēng)險(xiǎn)擴(kuò)散也就越大。因此,為了保持朋友間的距離,在設(shè)計(jì)時(shí)需要反復(fù)衡量:是否還可以再減少public屬性和方法,是否可以修改為private、package-private、protected等訪問(wèn)權(quán)限,是否可以加上final關(guān)鍵字等。
(三)、是自己的就是自己的
在實(shí)際應(yīng)用中經(jīng)常會(huì)出現(xiàn)這樣一個(gè)方法:放在本類中也可以,放在其他類也沒(méi)有錯(cuò),那怎么去衡量呢?可以檢查這樣一個(gè)原則:如果一個(gè)方法放在本類中,既不增加類間關(guān)系,也對(duì)本類不產(chǎn)生負(fù)面影響,那就放置在本類中。
三、總結(jié)
迪米特法則的核心觀念就是類間解耦,弱耦合,只有弱耦合了以后,類的復(fù)用率才可以提高。其要求的結(jié)果就是產(chǎn)生了大量的中轉(zhuǎn)或跳轉(zhuǎn)類,導(dǎo)致系統(tǒng)的復(fù)雜性提高,同時(shí)也為維護(hù)帶來(lái)了難度。讀者在采用迪米特法則的時(shí)候需要反復(fù)權(quán)衡,既做到讓結(jié)構(gòu)清晰,又做到高內(nèi)聚低耦合。
到此這篇關(guān)于Java面向?qū)ο笤O(shè)計(jì)原則之迪米特法則介紹的文章就介紹到這了,更多相關(guān)Java迪米特法則內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用dubbo+zookeeper+spring boot構(gòu)建服務(wù)的方法詳解
這篇文章主要給大家介紹了關(guān)于如何使用dubbo+zookeeper+spring boot構(gòu)建服務(wù)的相關(guān)資料,文中通過(guò)示例代碼及圖片介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05
在IDEA中使用debug工具去運(yùn)行java程序的實(shí)現(xiàn)步驟
調(diào)試工具(debug工具)是一種用于幫助程序員識(shí)別和修復(fù)程序中的錯(cuò)誤的工具,它們提供了一系列的功能,幫助程序員在代碼執(zhí)行的過(guò)程中跟蹤和檢測(cè)問(wèn)題,本文將給大家介紹使用debug工具去運(yùn)行java程序的實(shí)現(xiàn)步驟,需要的朋友可以參考下2024-04-04
Springboot集成knife4j實(shí)現(xiàn)風(fēng)格化API文檔
這篇文章主要介紹了Springboot如何集成knife4j實(shí)現(xiàn)風(fēng)格化API文檔,幫助大家更好的使用springboot框架,感興趣的朋友可以了解下2020-12-12
關(guān)于@Autowired注解和靜態(tài)方法及new的關(guān)系
這篇文章主要介紹了關(guān)于@Autowired注解和靜態(tài)方法及new的關(guān)系,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
java在linux系統(tǒng)下開(kāi)機(jī)啟動(dòng)無(wú)法使用sudo命令的原因及解決辦法
每次開(kāi)機(jī)自動(dòng)啟動(dòng)的java進(jìn)程,頁(yè)面上的關(guān)機(jī)按鈕都無(wú)法實(shí)現(xiàn)關(guān)機(jī)功能,但是此時(shí)如果以chb賬號(hào)通過(guò)ssh登錄該服務(wù)器,手動(dòng)殺掉tomcat進(jìn)程,然后再重新啟動(dòng)tomcat,頁(yè)面上的關(guān)機(jī)按鈕就有效了2013-08-08
java Array和Arrays的區(qū)別總結(jié)
在本篇內(nèi)容里小編給大家整理的是一篇關(guān)于java Array和Arrays的區(qū)別總結(jié)內(nèi)容,有需要的朋友們可以學(xué)習(xí)下。2021-03-03
java解決動(dòng)態(tài)配置字段需求問(wèn)題
這篇文章主要介紹了java解決動(dòng)態(tài)配置字段需求問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05

