六大設計原則--迪米特法則【Low Of Demeter】

聲明:本文內容是從網絡書籍整理而來,並非原創。

定義

迪米特法則也叫做最少知識原則(Least Knowledge Principle),指一個對象應該對其他對象有最少的瞭解,通俗的講:一個類對自己需要耦合或者調用的類應該知道的最少,你類內部是怎麼複雜、怎麼的糾纏不清都和我沒關係,那是你的類內部的事情,我只關心你提供的public方法,我可以去調用。

迪米特法則包含以下四層意思:

  1. 只和朋友交流。迪米特還有一個英文解釋叫做“Only talk to your immedate friends”,只和直接的朋友通信,什麼叫做直接的朋友呢?每個對象都必然會和其他對象有耦合關係,兩個對象之間的耦合就成爲朋友關係,這種關係有很多比如組合、聚合、依賴等等。我們來說個例子說明怎麼做到只和朋友交流。

    說是有這麼一個故事,老師想讓體育委員確認一下全班女生來齊沒有,就對他說: “你去把全班女生清一下。 ”體育委員沒聽清楚,或者是當時腦子正在回憶什麼東西,就問道: “親哪個?”老師¥#……¥%。我們來看這個笑話怎麼用程序來實現,先看類圖:
    這裏寫圖片描述
    Teacher類的代碼:

    public class Teacher { 
    
    //老師對學生髮布命令,清一下女生 
    public void commond(GroupLeader groupLeader){ 
        List<Girl> listGirls = new ArrayList(); 
        //初始化女生 
        for(int i=0;i<20;i++){ 
            listGirls.add(new Girl()); 
        } 
    
        //告訴體育委員開始執行清查任務 
        groupLeader.countGirls(listGirls); 
    } 
    } 

    老師就有一個方法, 發佈命令給體育委員, 去清查一下女生的數量。 下面是體育委員 GroupLeader 類的代碼:

    public class GroupLeader { 
    
    //有清查女生的工作 
    public void countGirls(List<Girl> listGirls){ 
        System.out.println("女生數量是:"+listGirls.size()); 
    } 
    } 

    我們來看這個業務調用類 Client::

    public class Client { 
    
    public static void main(String[] args) { 
        Teacher  teacher= new Teacher(); 
    
        //老師發佈命令 
        teacher.commond(new GroupLeader()); 
    } 
    } 

    運行的結果如下:

    女生數量是:20
    

    我們回過頭來看這個程序有什麼問題,首先來看 Teacher有幾個朋友,就一個 GroupLeader 類,這個就是朋友類,朋友類是怎麼定義的呢?出現在成員變量、方法的輸入輸出參數中的類被稱爲成員朋友類,迪米特法則說是一個類只和朋友類交流, 但是commond 方法中我們與 Girl 類有了交流,聲明瞭一個List動態數組,也就是與一個陌生的類 Girl 有了交流,這個不好,那我們再來修改一下,類圖還是不變,先修改一下 GroupLeader 類,看代碼:

    public class GroupLeader { 
    
    //有清查女生的工作 
    public void countGirls(){ 
        List<Girl> listGirls = new ArrayList<Girl>(); 
        //初始化女生 
        for(int i=0;i<20;i++){ 
            listGirls.add(new Girl()); 
        } 
    
        System.out.println("女生數量是:"+listGirls.size()); 
    } 
    } 

    Teacher類代碼:

    public class Teacher { 
    
    //老師對學生髮布命令,清一下女生 
    public void commond(GroupLeader groupLeader){ 
        //告訴體育委員開始執行清查任務 
        groupLeader.countGirls(listGirls); 
    } 
    } 

    程序做了一個簡單的修改,就是把 Teacher 中的對 List初始化(這個是有業務意義的,產生出全班的所有人員)移動到了 GroupLeader的 countGrils 方法中,避開了 Teacher 類對陌生類 Girl的訪問,減少系統間的耦合。 記住了, 一個類只和朋友交流, 不與陌生類交流, 不要出現 getA().getB().getC().getD()這種情況(在一種極端的情況下是允許出現這種訪問:每一個點號後面的返回類型都相同) ,那當然還要和JDK API 提供的類交流,否則你想脫離編譯器存在呀!

  2. 朋友間也是有距離的。人和人之間是有距離的,太遠就不是朋友了,太近就渾身不自在,這和類間關係也是一樣,即使朋友類也不能無話不說,無所不知。大家在項目中應該都碰到過這樣的需求:調用一個類,然後必須是先執行第一個方法,然後是第二個方法,根據返回結果再來看是否可以調用第三個方法,或者第四個方法等等,我們用類圖表示一下:
    這裏寫圖片描述
    Wizard 的代碼:

    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 代碼:

    public class InstallSoftware { 
    
    public void installWizard(Wizard wizard){ 
        int first = wizard.first();   
        //根據first返回的結果,看是否需要執行second 
        if(first>50){ 
            int second = wizard.second(); 
            if(second>50){ 
                int third = wizard.third(); 
                    if(third >50){ 
                        wizard.first(); 
                    }  
            }  
        } 
    
    } 
    }

    其中 installWizard 就是一個嚮導式的安裝步驟,我們看場景是怎麼調用的:

    public class Client { 
    
    public static void main(String[] args) { 
        InstallSoftware invoker = new InstallSoftware(); 
        invoker.installWizard(new Wizard()); 
    } 
    } 

    這個程序很簡單,運行結果和隨機數有關,我就不粘貼上來了。我們想想這個程序有什麼問題嗎?Wizard 類把太多的方法暴露給 InstallSoftware類了,這樣耦合關係就非常緊了,我想修改一個方法的返回值,本來是 int 的,現在修改爲 boolean,你看就需要修改其他的類,這樣的耦合是極度不合適的,迪米特法則就要求類“小氣”一點,儘量不要對外公佈太多的public方法和非靜態的public 變量,儘量內斂,多使用private,package-private、protected 等訪問權限。我們來修改一下類圖:
    這裏寫圖片描述
    再來看一下程序的變更,先看 Wizard 程序:

    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 = this.first();   
        //根據first返回的結果,看是否需要執行second 
        if(first>50){ 
            int second = this.second(); 
                if(second>50){ 
                    int third = this.third(); 
                        if(third >50){ 
                            this.first(); 
                        }  
                }  
        } 
    } 
    }

    三個步驟的訪問權限修改爲 private,同時把 installeWizad移動的 Wizard 方法中,這樣 Wizard 類就對外只公佈了一個 public 方法,類的高內聚特定顯示出來了。我們再來看 InstallSoftware 源碼:

    public class InstallSoftware { 
    
    public void installWizard(Wizard wizard){ 
        //不廢話,直接調用 
        wizard.installWizard(); 
    } 
    } 

    Client 類沒有任何改變,就不在拷貝了,這樣我們的程序就做到了弱耦合,一個類公佈越多的 public屬性或方法,修改的涉及面也就越大,也就是變更引起的風險就越大。因此爲了保持朋友類間的距離,你需要做的是:減少 public 方法,多使用 private、package-private(這個就是包類型,在類、方法、變量前不加訪問權限,則默認爲包類型)protected等訪問權限,減少非 static 的public 屬性,如果成員變量或方法能加上 final 關鍵字就加上,不要讓外部去改變它。

  3. 是自己的就是自己的。在項目中有一些方法,放在本類中也可以,放在其他類中也沒有錯誤,那怎麼去衡量呢?你可以堅持這樣一個原則:如果一個方法放在本類中,即不增加類間關係,也對本類不產生負面影響,就放置在本類中

  4. 謹慎使用 Serializable。實話說,這個問題會很少出現的,即使出現也會馬上發現問題。是怎麼回事呢?舉個例子來說,如果你使用 RMI 的方式傳遞一個對象 VO(Value? Object) ,這個對象就必須使用 Serializable接口,也就是把你的這個對象進行序列化,然後進行網絡傳輸。突然有一天,客戶端的VO 對象修改了一個屬性的訪問權限,從 private 變更爲 public 了,如果服務器上沒有做出響應的變更的話,就會報序列化失敗。這個應該屬於項目管理範疇,一個類或接口客戶端變更了,而服務端沒有變更,那像話嗎?!


迪米特法則的核心觀念就是類間解耦,弱耦合,只有弱耦合了以後,類的複用率纔可以提高,其要求的結果就是產生了大量的中轉或跳轉類,類只能和朋友交流,朋友少了你業務跑不起來,朋友多了,你項目管理就複雜,大家在使用的時候做相互權衡吧。

不知道大家有沒有聽過這樣一個理論: “任何 2個素不相識的人中間最多隻隔着 6個人,即只用 6 個人就可以將他們聯繫在一起” ,這個理論的學名叫做“六度分離” ,應用到我們項目中就是說我和我要調用的類之間最多有 6 次傳遞,呵呵,這隻能讓大家當個樂子來看,在實際項目中你跳兩次才訪問到一個類估計你就會想辦法了,這也是合理的,迪米特法則要求我們類間解耦,但是解耦是有限度的,除非是計算機的最小符號二進制的 0 和1,那纔是完全解耦,我們在實際的項目中時,需要適度的考慮這個法則,別爲了套用法則而做項目,法則只是一個參考,你跳出了這個法則,也不會有人判你刑,項目也未必會失敗,這就需要大家使用的是考慮如何度量法則了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章