## 一、定義
迪米特原則(Law of Demeter,LoD),也叫最少知識原則(Low knowledge Principle,LKP):
**一個對象應該對其他對象有最少的了解。**
> 通俗的講:一個類對自己需要耦合或調用的類知道的最少,你(被耦合或調用的類)的內部是如何復雜和我沒有關系,我就知道你提供的public方法,我只調用這些方法,其它的我不關心。
## 二、迪米特原則的具體要求
迪米特原則對類的低耦合提出了明確的要求:
#### `只與朋友類交流:`
>迪米特原則還有一個解釋:Only talk to your immediate friends(只與直接朋友通信)。
什么叫直接朋友呢?每個對象都必然會與其他對象有耦合關系,兩個對象之間的耦合就成為朋友關系,這種關系類型有很多,例如:組合,聚合,依賴等。朋友類也可以這樣定義:出現在成員變量,方法的輸入輸出參數中的類,稱為朋友類。
上體育課,我們經常有這樣一個場景:
體育老師上課前要體育委員確認一下全班女生到了多少位,也就是體育委員清點女生的人數。類圖如下:

老師類:
~~~csharp
public class Teacher{
//老師對體育委員發一個命令,讓其清點女生人數
public void command(GroupLeader groupLeader){
List<Girl> listGirls = new ArrayList();
//初始化女生
for(int i=0;i<20;i++){
listGirls.add(new Girl());
}
//告訴體育委員開始清點女生人數
groupLeader.countGirls(listGirls);
}
}
~~~
體育委員類:
~~~csharp
public class GroupLeader{
//清點女生數量
public void countGirls(List<Girl> listGirls){
System.out.println("女生人數是:"+listGirls.size());
}
}
~~~
女生類:
~~~cpp
publci class Girl{
}
~~~
場景類:
~~~cpp
public class Client{
public static void main(Strings[] args){
Teacher teacher = new Teacher();
//老師給體育委員發清點女生人數的命令
teacher.command(new GroupLeader());
}
}
~~~
我們再回頭看Teacher類,Teacher類只有一個朋友類GroupLeader,Girl類不是朋友類,但是Teacher與Girl類通信了,這就破壞了Teacher類的健壯性,Teacher類的方法竟然與一個不是自己的朋友類Girl類通信,這是不允許的,嚴重違反了迪米特原則。
我們對程序進行如下修改,將類圖修改如下:

修改后的體育委員類:
~~~csharp
public class GroupLeader{
private List<Girl> listGirls;
public GroupLeader(List<Girl> listGirls){
this.listGirls = listGirls;
}
//清點女生數量
public void countGirls(){
System.out.println("女生人數是:"+listGirls.size());
}
}
~~~
修改后的場景類:
~~~csharp
public class Client{
public static void main(Strings[] args){
//產生女生群體
List<Girl> listGirls = new ArrayList<Girl>();
//初始化女生
for(int i=0;i<20;i++){
listGirls.add(new Girl());
}
Teacher teacher = new Teacher();
//老師給體育委員發清點女生人數的命令
teacher.command(new GroupLeader(listGirls));
}
}
~~~
對程序修改,把Teacher中對Girl群體的初始化移動到場景類中,同時在GroupLeader中增加對Girl的注入,避開了Teacher類對陌生類Girl的訪問,降低了系統間的耦合,提高了系統的健壯性。
#### `朋友類間也是要有距離:`
我們在安裝軟件時,經常會有一個安裝向導的過程。比如第一步確認是否安裝,第二步確認License,第三步選擇安裝目錄…..。這個是一個典型的順序執行動作
導向類:
~~~csharp
public class Wizard{
private Random rand = new Random(System.currentTimeMillis());
//第一步
public int first(){
System.out.println("執行第一個方法.....");
return rand.nextInt(100);
}
//第二步
public int second(){
System.out.println("執行第二個方法.....");
return rand.nextInt(100);
}
//第三步
public int third(){
System.out.println("執行第三個方法.....");
return rand.nextInt(100);
}
}
~~~
InstallSoftware類:
~~~cpp
public class InstallSoftware{
public void installWizard(Wizard wizard){
int first = wizard.first();
//根據first返回的結果,看是否要執行下一步
if(first >50){
int second = wizard.second();
if(second >50){
wizard.third();
}
}
}
}
~~~
場景類:
~~~cpp
public class Client{
public static void main(Strings[] args){
InstallSoftware invoker = new InstallSoftware();
invoker.installWizard(new Wizard());
}
}
~~~
以上的程序非常簡單,但是隱藏了一個問題。Wizard類把太多的方法暴露給InstallSoftware類,導致兩者的關系太親密,耦合關系變量異常牢固。我們把Wizard類進行重構:
修改后的Wizard類:
~~~csharp
public class Wizard{
private Random rand = new Random(System.currentTimeMillis());
//第一步
private int first(){
System.out.println("執行第一個方法.....");
return rand.nextInt(100);
}
//第二步
private int second(){
System.out.println("執行第二個方法.....");
return rand.nextInt(100);
}
//第三步
private int third(){
System.out.println("執行第三個方法.....");
return rand.nextInt(100);
}
//軟件安裝過程
public void installWizard(){
int first = wizard.first();
//根據first返回的結果,看是否要執行下一步
if(first >50){
int second = wizard.second();
if(second >50){
wizard.third();
}
}
}
}
~~~
修改后的InstallSoftware類:
~~~cpp
public class InstallSoftware{
public void installWizard(Wizard wizard){
wizard.installWizard()
}
}
~~~
通過重構,類間的耦合關系變弱了,結構變得清晰,變量的風險也變小了。
一個類公開的public方法和屬性越多,修改時涉及的面也就越大,變更引起的風險擴散也就越大。因此,為了保持朋友類間的距離,在設計時需要反復衡量:是否還可以再減少public方法和屬性,是否可以修改為private,package-private,protected等訪問權限,是否可以加上final關鍵字。
#### **`注意:`**
>迪米特原則要求類“羞澀”一點,盡量不要對外公開太多的public方法和非靜態的public變量,盡量內斂,多使用private,package-private,protected等訪問權限。
在實踐中經常出現這樣一個方法,放在本類中也可以,放到其它類中也可以。那怎么處理呢?你可以堅持一個原則:**如果一個方法放在本類中,即不增加類間關系,也對本類不產生負面影響,那就放到本類中。**
#### **`迪米特原則的實踐:`**
>迪米特原則的核心觀念就是類間解耦,弱耦合,只有弱耦合后,類的復用率才可以提高。其結果就是產生了大量的中轉或跳轉類,導致系統復雜,為維護帶來了難度。所以,我們在實踐時要反復權衡,即要讓結構清晰,又做到高內聚低耦合。
- 前言
- 第一章 設計七大原則
- 第1節 開閉原則
- 第2節 依賴倒置原則
- 第3節 單一職責原則
- 第4節 接口隔離原則
- 第5節 迪米特法則
- 第6節 里氏替換原則
- 第7節 合成復用原則
- 第二章 簡單工廠模式
- 第1節 使用場景
- 第2節 示例代碼
- 第三章 創建者模式
- 第1節 工廠方法模式
- 第2節 抽象工廠模式
- 第3節 建造者模式
- 第4節 原型模式
- 第5節 單例模式
- 第四章 結構型模式
- 第1節 適配器模式
- 第2節 橋接模式
- 第3節 組合模式
- 第4節 裝飾者模式
- 第5節 外觀模式
- 第6節 享元模式
- 第7節 代理模式
- 第五章 行為模式
- 第1節 責任鏈模式
- 第2節 命令模式
- 第3節 迭代器模式
- 第4節 中介者模式
- 第5節 備忘錄模式
- 第6節 觀察者模式
- 第7節 狀態模式
- 第8節 策略模式
- 第9節 模板方法模式
- 第10節 訪問者模式
- 第11節 解釋器模式