面向對象六大原則----接口隔離原則,迪米特原則

Java 中面向對象編程六大原則:

單一職責原則 英文名稱是Single Responsibility Principle,簡稱SRP

開閉原則 英文全稱是Open Close Principle,簡稱OCP

里氏替換原則 英文全稱是Liskov Substitution Principle,簡稱LSP

依賴倒置原則  英文全稱是Dependence Inversion Principle,簡稱DIP

接口隔離原則 英文全稱是InterfaceSegregation Principles,簡稱ISP

迪米特原則 英文全稱爲Law of Demeter,簡稱LOD,也稱爲最少知識原則(Least Knowledge Principle)


系統有更高的靈活性——接口隔離原則

接口隔離原則英文全稱是InterfaceSegregation Principles,簡稱ISP。它的定義是:客戶端不應該依賴它不需要的接口。另一種定義是:類間的依賴關係應該建立在最小的接口上。接口隔離原則將非常龐大、臃腫的接口拆分成爲更小的和更具體的接口,這樣客戶將會只需要知道他們感興趣的方法。接口隔離原則的目的是系統解開耦合,從而容易重構、更改和重新部署。

比如 你定義了一個接口
public interface I {
    public void method1();  
    public void method2();  
    public void method3();  
    public void method4();  
    public void method5();  
}
但是
A只想實現接口中的method1方法
B只想實現接口中的method2,method3方法
C只想實現接口中的method4,method5方法

如果你這個時候就寫一個接口,讓A,B,C都去實現I接口的話,這樣會導致A,B,C中會實現不需要的方法,這樣設計接口會導致接口比較臃腫,因此我們要把這個接口分開寫。繼續以我們之前的ImageLoader示例來說明一下。我們在使用了OutputStream或者其他可關閉的對象之後,我們必須保證它們最終被關閉了,我們的SD卡緩存類中就有這樣的代碼:

  //將下載的圖片緩存到sd卡里
    @Override
    public void put(String key, Bitmap bitmap){
        if(!mCachDir.exists()){
            return;
        }
        //對傳入的key值進行MD5處理
        String savedFilePath = mCacheDirPath+File.separator+hashKeyForDisk(key);
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(new File(savedFilePath));
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        finally {
            if(fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

我們看到的這段代碼可讀性非常差,各種try…catch嵌套,都是些簡單的代碼,但是會嚴重影響代碼的可讀性,並且多層級的大括號很容易將代碼寫到錯誤的層級中。大家應該對這類代碼也非常反感,那我們看看如何解決這類問題。 我們可能知道Java中有一個Closeable接口,該接口標識了一個可關閉的對象,它只有一個close方法。如下圖:

既然這些文件流都是實現了Closeable接口,那隻要建一個方法統一來關閉這些對象不就可以了麼,代碼如下:

public class CloseUtils {

    private CloseUtils(){}

    public static void close(Closeable closeable){
        try {
            if(closeable != null) {
                closeable.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
我們再看看把這段代碼運用到上述的put方法中的效果如何:

    //將下載的圖片緩存到sd卡里
    @Override
    public void put(String key, Bitmap bitmap){
        if(!mCachDir.exists()){
            return;
        }
        //對傳入的key值進行MD5處理
        String savedFilePath = mCacheDirPath+File.separator+hashKeyForDisk(key);
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(new File(savedFilePath));
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        finally {
            CloseUtils.close(fileOutputStream);
        }
    }

代碼簡潔了很多!而且這個closeUtils方法可以運用到各類可關閉的對象中,保證了代碼的重用性。CloseUtils的close方法的基本原理就是依賴於Closeable抽象而不是具體實現(這不是前面講的依賴倒置原則麼),並且建立在最小化依賴原則的基礎,它只需要知道這個對象是可關閉,其他的一概不關心,也就是這裏的接口隔離原則。

試想一下,如果在只是需要關閉一個對象時,它卻暴露出了其他的接口函數,比如OutputStream的write方法,這就使得更多的細節暴露在客戶端代碼面前,不僅沒有很好地隱藏實現,還增加了接口的使用難度。而通過Closeable接口將可關閉的對象抽象起來,這樣只需要客戶端依賴於Closeable就可以對客戶端隱藏其他的接口信息,客戶端代碼只需要知道這個對象可關閉(只可調用close方法)即可。


更好的可擴展性——迪米特原則

迪米特原則英文全稱爲Law of Demeter,簡稱LOD,也稱爲最少知識原則(Least Knowledge Principle)。雖然名字不同,但描述的是同一個原則:一個對象應該對其他對象有最少的瞭解。通俗地講,一個類應該對自己需要耦合或調用的類知道得最少,類的內部如何實現、如何複雜都與調用者或者依賴者沒關係,調用者或者依賴者只需要知道他需要的方法即可,其他的我一概不關心。類與類之間的關係越密切,耦合度越大,當一個類發生改變時,對另一個類的影響也越大。

迪米特法則還有一個英文解釋是:Only talk to your immedate friends,翻譯過來就是:只與直接的朋友通信。什麼叫做直接的朋友呢?每個對象都必然會與其他對象有耦合關係,兩個對象之間的耦合就成爲朋友關係,這種關係的類型有很多,例如組合、聚合、依賴等。

我們來說個例子說明怎麼做到只和朋友交流。 說是有這麼一個故事,老師想讓班長確認一下全班女生來齊沒有,就對他說:“你去把全班女生清 一下。”班長沒聽清楚,或者是當時腦子正在回憶什麼東西,就問道:“親哪個?”老師¥#……¥%。 我們來看這個笑話怎麼用程序來實現:

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

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

    /**
     * 女生
     */
    public class Girl {
    }

下面是模擬命令過程:

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

運行Client的結果就是:女生數量是:20

我們回過頭來看這個程序有什麼問題,首先來看 Teacher 有幾個朋友,就一個Monitor類,這個就是朋友類, 迪米特法則要求一個類只和朋友類交流, 但是 commond 方法中我們與 Girl 類有了交流,聲明瞭一個 List動態數組,也就是與一個陌生的類 Girl 有了交流,這樣設計不好,耦合嚴重,修改時很容易出錯。下面看看重新設計Teacher和Monitor類:

 /**
     * 老師類
     */
    public class Teacher {
        //老師對班長髮布命令, 清一下女生
        public void commond(Monitor monitor){
            //告訴體育委員開始執行清查任務
            monitor.countGirls();
        }
    }

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

    /**
     * 女生
     */
    public class Girl {
    }

    /**
     * 調用類
     */
    public class Client {
        public void main(String[] args) {
            Teacher teacher = new Teacher();
            //老師發佈命令
            teacher.commond(new Monitor());
        }
    }
程序做了一個簡單的修改,就是把 Teacher 中的對女生類 List初始化(這個是有業務意義的,產生出 全班的所有人員)移動到了 Monitor 的 countGrils 方法中,避開了 Teacher 類對陌生類 Girl 的訪問, 減少系統間的耦合, 使得系統具有更低的耦合性和更好的可擴展性。

在我們之前的ImageLoader代碼裏面,ImageLoader只與ImageCahe交流,而具體實現ImageCahe的各個Cache類可以有自己不同的實現細節,但是這些細節對ImageLoader來說都是不可見的,這裏也是用到了迪米特原則。

代碼github地址:點擊打開鏈接

更多精彩Android技術可以關注我們的微信公衆號,掃一掃下方的二維碼或搜索關注公共號: Android老鳥

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