七種設計原則(六)迪米特法則

1.定義:編程

迪米特法則(Law of Demeter)又叫做最少知道原則(Least Knowledge Principle 簡寫LKP),就是說一個對象應當對其餘對象儘量少的瞭解,不和陌生人說話。英文簡寫爲: LoD。一個對象應該對其餘對象保持最少了解, 通俗的講就是一個類對本身依賴的類知道的越少越好,也就是對於被依賴的類,向外公開的方法應該儘量的少。(低耦合)安全

迪米特法則還有一種解釋:Only talk to your immediate friends只與直接朋友通訊(大部分是接口或者父類,增長靈活性).首先來解釋編程中的朋友:兩個對象之間的耦合關係稱之爲朋友,一般有依賴,關聯,聚合和組成等.而直接朋友則一般表現爲關聯,聚合和組成關係,即兩個對象之間聯繫更爲緊密,一般以成員變量,方法的參數和返回值的形式出現。(高內聚)(少依賴,多聚合關聯組成)ide

不難發現,迪米特法則強調了一下兩點:this

第一要義:spa

從被依賴者的角度來講:只暴露應該暴露的方法或者屬性(最少知道原則),即在編寫相關的類的時候肯定方法/屬性的權限操作系統

第二要義:設計

從依賴者的角度來講,只依賴應該依賴的對象(只和最直接的朋友打交道)code

 

2.問題和解決方案:對象

問題:類與類之間的關係越密切,耦合度越大,當一個類發生改變時,對另外一個類的影響也越大。接口

例如:設計一個軟件,當一個按鈕(Button)被單擊時,對應的列表框(List)、組合框(ComboBox)、文本框(TextBox)、文本標籤(Label)等都將發生改變,在初始設計方案中,界面控件之間的交互關係可簡化爲以下所示結構:

      如圖中,因爲界面控件之間的交互關係複雜,致使在該窗口中增長新的界面控件時須要修改與之交互的其餘控件的源代碼,系統擴展性較差,也不便於增長和刪除新控件。

 

解決方案:儘可能下降類與類之間的耦合

現使用迪米特原則對其進行重構。

能夠經過引入一個專門用於控制界面控件交互的中間類(Mediator)來下降界面控件之間的耦合度。引入中間類以後,界面控件之間再也不發生直接引用,而是將請求先轉發給中間類,再由中間類來完成對其餘控件的調用。當須要增長或刪除新的控件時,只需修改中間類便可,無須修改新增控件或已有控件的源代碼,重構後結構以下圖所示:

 

迪米特法則第一要義:「最少知道原則」

以關閉計算機爲例

問題1.

當咱們按下計算機的關機按鈕的時候,計算機會執行一些列的動做會被執行:好比保存當前未完成的任務,而後是關閉相關的服務,接着是關閉顯示器,最後是關閉電源,這一系列的操做以此完成後,計算機纔會正式被關閉。

如今,咱們來用簡單的代碼表示這個過程,在不考慮迪米特法則狀況下,咱們可能寫出如下代碼

//計算機類
public class Computer{

    public void saveCurrentTask(){
        //do something
    }
    public void closeService(){
        //do something
    }
    public void closeScreen(){
        //do something
    }

    public void closePower(){
        //do something
    }

    public void close(){
        saveCurrentTask();
        closeService();
        closeScreen();
        closePower();
    }
}

//人
public class Person{
    private Computer c;

    ...

    public void clickCloseButton(){
      //如今你要開始關閉計算機了,正常來講你只須要調用close()方法便可,
      //可是你發現Computer全部的方法都是公開的,該怎麼關閉呢?因而你寫下了如下關閉的流程:        
        c.saveCurrentTask();
        c.closePower();
        c.close();

        //亦或是如下的操做        
        c.closePower();

        //還多是如下的操做
        c.close();
        c.closePower();
    }

}

發現問題了麼?

咱們觀察clickCloseButton()方法,咱們發現這個方法沒法編寫:c是一個徹底暴露的對象,其方法是徹底公開的,那麼對於Person來講,當他想要執行關閉的時候,卻發現不知道該怎麼操做:該調用什麼方法?靠運氣猜麼?若是Person的對象是個不按常理出牌的,那這個Computer的對象豈不是要被搞壞麼?

如今咱們來看看迪米特法則的第一點:從被依賴者的角度,只應該暴露應該暴露的方法。那麼這裏的c對象哪些方法應該是被暴露的呢?很顯然,對於Person來講,只須要關注計算機的關閉操做,而不關心計算機會如何處理這個關閉操做,所以只須要暴露close()方法便可。

那麼上述的代碼應該被修改成:

//計算機類
public class Computer{

    private void saveCurrentTask(){
        //do something
    }
    private void closeService(){
        //do something
    }
    private void closeScreen(){
        //do something
    }

    private void closePower(){
        //do something
    }

    public void close(){
        saveCurrentTask();
        closeService();
        closeScreen();
        closePower();
    }
}

//人
public class Person{
    private Computer c;
    ...

    public  void clickCloseButton(){
       c.close();
    }

}

類圖以下:

迪米特第二要義:「只和最直接的朋友打交道」

問題2.

咱們都知道,計算機包括操做系統和相關硬件,咱們能夠將其劃分爲System對象和Container對象。當咱們關閉計算機的時候,本質上是向操做系統發出了關機指令,而實則咱們只是按了一下關機按鈕,也就是咱們並無依賴System的對象,而是依賴了Container。這裏Container就是咱們上面所說的直接朋友---只和直接朋友通訊. 

看迪米特法則的第二層含義:從依賴者的角度來講,只依賴應該依賴的對象。Only talk to your immediate friends(肯定你真正的朋友,並只和他們通訊,而且不要和陌生人講話),好處就是簡化對象之間的通訊,減輕依賴,提升靈活性。

咱們再回顧"關機計算機"這個操做,前面的代碼只是聽從了"暴漏應該暴漏的方法"這一點,如今咱們結合第二點來進行改進:System和Container相比,System並不是Person的直接朋友,而Container纔是(Person直接打交道的是Container).所以咱們須要將原有的Computer拆分紅System和Cotainer,而後使Person只與Container通訊,所以代碼修改成:

//操做系統
public class System{

    private void saveCurrentTask(){
        //do something
    }
    private void closeService(){
        //do something
    }
    private void closeScreen(){
        //do something
    }

    private void closePower(){
        //do something
    }

    public void close(){
        saveCurrentTask();
        closeService();
        closeScreen();
        closePower();
    }
}

//硬件設備容器
public class Container{
    private System mSystem;

    public void sendCloseCommand(){
        mSystem.close();
    }
}

//人
ublic class Person{
    private Container c;
    ....

    public void clickCloseButton(){
       c.sendCloseCommand();
    }

}

類圖以下:

重構,改善既有設計

在上文中,咱們還提到,直接朋友出現的地方,咱們能夠採用其接口或者父類來代替.那麼在這裏,咱們就能夠爲Container和System提供相應的接口。

//System interface
public interface ISystem{
    void close();
}

//System
public class System implements ISystem{

    private void saveCurrentTask(){
        //do something
    }

    private void closeService(){
        //do something
    }

    private void closeScreen(){
        //do something
    }

    private void closePower(){
        //do something
    }

    @override
    public void close(){
        saveCurrentTask();
        closeService();
        closeScreen();
        closePower();
    }
}

//IContainer interface
public interface IContainer{
    void sendCloseCommand();
}

//Contanier
public class Container implements IContainer{
    private System mSystem;

    @override
    public void sendCloseCommand(){
        mSystem.close();
    }
}

//Person
ublic class Person{
    private IContainer c;
    ....

    public void clickCloseButton(){
       c.sendCloseCommand();
    }

}

類圖以下:

對比這兩種方案,明顯這種方案二的解耦程度更高,靈活大大加強.不難發現,這應用了咱們前面提到的依賴倒置,即面向接口編程.

除此以外,咱們發現隨着不斷的改進,類的數量也在不斷的增長,從2個增長到5個,這意味着爲了解耦和提升靈活性一般要編寫的類的數量會翻倍.所以,你須要在這作一個權衡,切莫刻意爲了追求設計,而致使整個系統很是的冗餘,最終可能得不償失。

 

3.特色

若是兩個類沒必要彼此直接通訊,那麼這兩個類就不該當發生直接的相互做用。若是其中的一個類須要調用另外一個類的某一個方法的話,能夠經過第三者轉發這個調用。

朋友圈的肯定

「朋友」條件:

1)當前對象自己(this)

2)以參量形式傳入到當前對象方法中的對象

3)當前對象的實例變量直接引用的對象

4)當前對象的實例變量若是是一個彙集,那麼彙集中的元素也都是朋友

5)當前對象所建立的對象

任何一個對象,若是知足上面的條件之一,就是當前對象的「朋友」;不然就是「陌生人」。

 

4.優缺點

優勢:

低內聚,高耦合。

更加靈活。

更加安全

缺點:

在系統裏造出大量的小方法,這些方法僅僅是傳遞間接的調用,與系統的商務邏輯無關。

遵循類之間的迪米特法則會是一個系統的局部設計簡化,由於每個局部都不會和遠距離的對象有直接的關聯。可是,這也會形成系統的不一樣模塊之間的通訊效率下降,也會使系統的不一樣模塊之間不容易協調。

 

5.設計實現

優先考慮將一個類設置成不變類。

儘可能下降一個類的訪問權限。

謹慎使用Serializable。

儘可能下降成員的訪問權限。

 

6.總結:

和最直接的朋友打交道,讓最少的人知道~

門面模式調停者模式實際上就是迪米特法則的應用。

轉載鏈接:http://www.jianshu.com/p/14589fb6978e

相關文章
相關標籤/搜索