天天看點

面向對象的六大設計原則(四):終結篇

四、接口隔離原則:Interface Segregation Principle(ISP)

       定義:用戶端不應該依賴它不需要的接口;一個類對另一個類的依賴應該建立在最小的接口上。這個原則比較簡單很好了解,但熟練使用卻不簡單。如何适度的使用接口,需要我們進行反複的思考與設計,才能很好的實踐這一原則。這就好像我們的筆記本電腦,一般隻會有USB、HDMI、VGA和網線接口。簡簡單單的幾個接口,支援裝置的接入,網絡連接配接,視訊投影。再多的話,就會造成接口備援,少了的話, 又無法滿足現實所需。

       接下來我們看些Android中的一些源碼

public interface OnClickListener {
        /**
         * Called when a view has been clicked.
         *
         * @param v The view that was clicked.
         */
        void onClick(View v);
    }
    
    public interface OnLongClickListener {
        /**
         * Called when a view has been clicked and held.
         *
         * @param v The view that was clicked and held.
         *
         * @return true if the callback consumed the long click, false otherwise.
         */
        boolean onLongClick(View v);
    }
    


           

這是Android中我們經常使用的兩個接口,分别用于使用者監聽點選事件和長按事件,同樣都是點選事件,Android工程師為什麼不把這兩個接口合并在一起而要分開呢?這裡Android工程師就很好的遵循了接口隔離原則。雖然兩個接口的功能類似,都是處理使用者的點選事件,但試想一下,如果Android工程師把這兩個接口合并到一起,每當我們要單獨處理一個點選事件的時候,都必須連帶把另外一個接口也要實作,這就會造成大量無用的實作,造成大量無用備援代碼。

  采用接口隔離原則對接口進行限制時,我們要讓接口盡量小,但凡是都要有個度。細化的接口确實能極大的提高程式的靈活性,但過于細化接口,反而會造接口數量過多,反而使得程式難以維護,這就又背于我們的初衷了。至于這個度如何把握,需要我們在設計時,去反複推敲,反複思考,才能很好的踐行這一職責。

五、迪米特原則:Law of Demeter(LOD)

      定義:最少知道原則,一個對象應該對其他對象保持最少的了解。簡單點說,一個類應該對自己需要調用或者耦合的類知道的越少越好,類的内部如何實作于調用者或者依賴着沒關系,調用者隻需要知道他需要調用的方法即可。類與類之間的關系越密切,耦合度也就越大,當類發生改變時,對另外一個類的影響也就越大。迪米特原則還有一個更簡單的定義:隻與直接的朋友通信。首先來解釋一下什麼是直接的朋友:每個對象都會與其他對象有耦合關系,隻要兩個對象之間有耦合關系,我們就說這兩個對象之間是朋友關系。耦合的方式很多,依賴、關聯、組合、聚合等。其中,我們稱出現成員變量、方法參數、方法傳回值中的類為直接的朋友,而出現在局部變量中的類則不是直接的朋友。也就是說,陌生的類最好不要作為局部變量的形式出現在類的内部。

六、開閉原則:Open Close Principle(OCP)

      定義:軟體中的對象(類、子產品、函數等)應該對于擴充是開放的,但是,對于修改是封閉的。在軟體的生命周期内,因為變化、更新和維護等原因需要對軟體原有代碼進行修改時,可能會将錯誤引入原本已經經過測試的舊代碼中,破壞原有系統。開閉原則是面向對象設計中最基礎的設計原則,它指導我們如何建立穩定靈活的系統。開閉原則可能是設計模式六項原則中定義最模糊的一個了,它隻告訴我們對擴充開放,對修改關閉,可是到底如何才能做到對擴充開放,對修改關閉,并沒有明确的告訴我們。想起了剛畢業找工作的時候被難住的面試題。題目大概意思是這樣。需要做一個解析網絡資料的工具類,在服務端傳回的資料能拿到資料格式(xml,json,或者其他),根據資料格式進行相應的解析。當時也天真,這算什麼面試題,那麼簡單,用if else 判斷格式,再進行資料解析,是xml,接按xml 解析, 是json,就按json解析。當時也沒多想,大筆一揮,就這樣寫了。

當時寫的代碼大概是這樣

public void analysisData(Mode type){
        if(type = xml){
            //進行xml 資料格式解析
        }else if(type = json){
            // 進行 json 資料格式解析
        }
    }
           

結果,當然是被pass掉了。現在我們來看看我當時寫的代碼,感覺是解決的題目所提出的問題,但是如果後期來了一種新的資料格式,代碼就會變成這樣

public void analysisData(Mode type){
        if(type = xml){
            //進行xml 資料格式解析
        }else if(type = json){
            // 進行 json 資料格式解析
        }else if(type = 流檔案){
            // 進行 資料流解析
        }else if(...type.) {
            //  
        }
    }
           

每次新加一種資料格式,就需要修改源碼。不僅繁瑣,而且一不留神,改錯了判斷語句,加錯了括号,以前的代碼也會跟着出現問題。維護起來相當繁瑣,如果後期有100中資料格式,意味着要寫一百次 if else 判斷。記得當時,面試官問了一句,你覺得你的代碼,可以再怎麼優化一下, 我天真的說了句,可以用switch語句替換if else 汗顔 - - 。

我們回到開閉模式,看下如何用開閉模式優化如上代碼

public class HttpUtils {
    
    AnalysisDate analysisData;

    public void analysisData(Mode type){
        analysisDate.analysis(type);
    }
    
    public void setAnalysisData(AnalysisData analysisDate){
        this.analysisDate = analysisDate;
    }
    
}    

/**
 * 解析資料接口
 *
 */
public interface  AnalysisData{
    void analysis(Mode type);
}


public class JsonAnalysisDate implements AnalysisData{

    public void analysis(Mode type) {
        // json 資料解析
    }
}
public class XmlAnalysisDate implements AnalysisData{
    
    public void analysis(Mode type) {
        // xml 資料解析
    }
}
           

仔細觀察上面優化之後代碼,發現基本上前面所講的五種模式都涉及在裡面了。在仔細思考以及仔細閱讀很多設計模式的文章後,終于對開閉原則有了一點認識。其實,我們遵循設計模式前面5大原則,以及使用23種設計模式的目的就是遵循開閉原則。也就是說,隻要我們對前面5項原則遵守的好了,設計出的軟體自然是符合開閉原則的,這個開閉原則更像是前面五項原則遵守程度的“平均得分”,前面5項原則遵守的好,平均分自然就高,說明軟體設計開閉原則遵守的好;如果前面5項原則遵守的不好,則說明開閉原則遵守的不好。