天天看點

java代碼優化六大原則單一職責開閉原則裡氏替換原則依賴倒置原則接口隔離原則迪米特原則總結

單一職責

代碼優化第一步,單一職責原則 (Single Responsibility Principle)。對于一個java類,應該僅有一個引起它變化的原因,也就是說,一個類中,應該是一組相關性很高的函數、資料的封裝。但是這個原則的界限劃分的并不是那麼清晰,很大程度上要依賴于開發者的個人經驗來定。對于單一職責界限的劃分最大的問題就是類的職責是什麼,如何劃分類的職責。

單一職責原則在我們實際工作中随處可見,例如在我們比較關心的架構MVC,MVP中,負責頁面展示的Activity,Fragment,以及各種View,它們隻負責UI的展示,具體的業務邏輯則傳遞給Controller或者Presenter.。再例如各種流行的圖檔加載架構,在其中都可以找到專門負責圖檔加載類,圖檔緩存的類。是以,單一職責原則是我們代碼優化的第一步,也是最重要的一步。

開閉原則

開閉原則(Open Close Principle),是Java世界裡最基礎的設計原則,它指導我們如何建立一個穩定、靈活的系統。開閉原則定義:軟體中的對象(類,子產品、函數等)應該對于擴充是開放的,對于修改的封閉的。在軟體的生命周期内,因為變化、更新、維護等原因需要對軟體原有的代碼進行修改時,可能會将錯誤引入原本已經測試過的舊代碼,破壞原有系統,是以,當軟體需要變化時,我們應該進肯能通過擴充的方式來實作變化,而不是通過修改已有的代碼來實作。但這也不是絕對的,在實際開發過程中,隻通過繼承的方式來更新、維護原有系統是一個理想化的情況,是以,實際開發中,修改原有代碼、擴充代碼往往是同時存在的。而如何確定原有軟體子產品的正确性,以及盡量少地影響原有代碼子產品,答案就是盡量遵守開閉原則。

裡氏替換原則

裡氏替換原則(Liskov Substitution Principle),定義:如果對于每一個類型為ClassA的對象a,都有類型為ClassB的對象b,使得以ClassB定義的所有程式P在所有的對象b都替換成a時,程式P的行為沒有發生變化,那麼類型ClassA是類型ClassB的子類型。然而這段叙述并無卵用,更直接的定義是:所有引用基類的地方必須能透明的使用其子類的對象。

我們知道,面向對象的三大特點是:繼承,封裝,多态,裡氏替換原則就是依賴于繼承、多态這兩大特性。裡氏替換原則簡單來說就是,所有引用基類的地方,必須能使用子類對象。也就是說隻要父類能出現的地方,其子類就可以出現。而且替換為子類不會産生任何錯誤和差異。使用者可能根部就不需要知道是父類還是子類,但是反過來就行不了,有子類出現的地方父類未必就能适應。其實歸根結底,裡氏替換原則就是基于這兩個字:抽象

例如在Android中,頁面Window的展示依賴于View,而View定義了一個視圖抽象,measure以及draw是其子類共享的方法,子類通過複寫measure以及draw方法,可實作各式各樣功能以及視圖的view,任何繼承自View的子類都可以傳遞給Window,由Window負責組織View,并将View顯示到螢幕上,這就是所說的裡氏替換原則。

裡氏替換原則的核心原理是抽象,抽象又依賴于繼承,在OOP當中,繼承的優缺點都相當明顯。優點是:

  • 代碼重用,減少建立類的成本,每個子類都擁有父類的方法和屬性
  • 子類和父類基本相似,但又與父類有所差別
  • 提高代碼的可擴充性

繼承的缺點:

- 繼承是入侵性的,隻要繼承就必須擁有父類的所有屬性和方法

- 可能造成子類代碼備援,靈活性降低,因為子類必須擁有父類的方法和屬性

但事物總有兩面性,符合權衡和利用都是需要根據具體情況來做出選擇。在開發過程中運用抽象是走向代碼優化的重要一步。

依賴倒置原則

依賴倒置原則(Dependence Inversion Principle),依賴倒置原則指定了一種特定的解耦形式,使得高層次的子產品不依賴于低層次的子產品的實作細節的目的,依賴子產品被颠倒了。然而定義往往的不好了解的,依賴倒置原則有以下幾個關鍵點:

- 高層子產品不應該依賴低層子產品,兩者都應該依賴其抽象

- 抽象不應該依賴細節

- 細節應該依賴抽象

在Java 語言中,抽象就是指接口或抽象類,兩者都是不能直接被執行個體化的。細節就是實作類,實作接口或繼承抽象類而産生的類就是細節,其特點是,可以直接被執行個體化。高層子產品就是調用端,低層子產品就是具體實作類。依賴倒置原則在Java語言中的表現就是:子產品間的依賴通過抽象發生,實作類之間不發生直接的依賴關系,其依賴關系是通過接口或實作類産生的。其實一句話就是:面向接口,或者面向抽象程式設計。

如果類于類直接依賴于細節,那麼它們之間就有直接耦合,當具體實作需要變化時,意味着要同時修改依賴者的代碼,這限制了系統的可擴充性。

接口隔離原則

接口隔離原則(Interface Segregation Principle),它的定義是:用戶端不應該依賴它不需要的接口。另一種定義是:類間的依賴關系應該建立在最小的接口上。接口隔離原則将非常龐大,臃腫的接口拆分成更小的接口和更具體的接口,這樣客戶隻需要知道他們感興趣的方法。接口隔離原則的目的是系統解開耦合,進而容易重構、更改和重新部署。

定義總是不好了解的,我們通過一段代碼來了解一下接口隔離原則的具體使用。比如我們常見的輸出流OutputStream,使用之後需要将其關閉:

FileOutputStream fos = null;
try{
    fos = new FileOutputStream(URI);
    ...
}catch(FileNotFoundException e){
    e.printStackTrace();
}finally{
    if(fos!=null) {
        try{
            fos.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}
           

我們看到,這段代碼的可讀性非常差,各種try catch嵌套的都是極其簡單的代碼,那麼我們如何解決這個問題呢?在Java中有一個Closeable接口:

public interface Closeable extends AutoCloseable {

    /**
     * Closes this stream and releases any system resources associated
     * with it. If the stream is already closed then invoking this
     * method has no effect.
     *
     * @throws IOException if an I/O error occurs
     */
    public void close() throws IOException;
}
           

該接口辨別了一個可關閉的對象,它隻有一個close方法,而我們的FileOutputStream也實作了這個接口。這樣我們就好辦了,我們可以依賴Closeable 接口實作一個工具類:

public final class CloseUtils{
    private CloseUtils(){}

    public static void closeQuietly(Closeable closeable){
        if(null!=closeable){
            try{
                closeable.close();
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }
}
           

在實際的運用中,我們隻需要這樣:

...
finally{
    CloseUtils.closeQuietly(fos);
}
           

代碼簡潔了很多,而且這個工具類可以運用到各類可關閉的對象中,保證了代碼的重用性。CloseUtils的closeQuietly方法的基本原理就是依賴于CLoseable抽象而不是具體實作,并且建立在最小化依賴原則的基礎上,它隻需要知道這個對象是可關閉的,其他的一概不關心,也就是我們所提出的接口隔離原則。

迪米特原則

迪米特原則(Law of Demeter),也成為最少知識原則:一個對象應該對其他對象有最少的了解。也就是說,一個類應該對自己需要耦合或者調用的類知道的最少,類的内部如何實作與調用者或者依賴者沒關系,調用者和依賴者隻需要知道它需要的方法即可,其他的一概不管。類與類的關系越密切,耦合度越大,當一個類發生改變時,對另一個類的影響也越大。

就比如說MVP架構中的Model層的實作,我們都知道Model抽象是給View提供具體的資料,而我們的View層并不需要知道資料是怎麼得來的,就算我們背景接口如何改變,隻要資料結構不變,那我們就不需要通知View層進行改變。

總結

在我們實際開發中,最難的不是完成應用的開發工作,而是後續的更新、維護過程中讓系統相容變化,這也意味着在滿足需求而不破壞系統穩定性的前提下保持高可擴充性、高内聚、低耦合,在經曆了各版本的變化之後依然保持清晰,靈活,穩定的系統架構。當然這是一個理想狀态,但是我們必須朝這個方向努力,那麼遵循我們上面提出的六大原則就是我們優化代碼,走向靈活軟體之路的第一步!

之後我會結合以上六大基本原則,講解Android中的設計模式的運用,請持續關注。

歡迎一起交流學習。

繼續閱讀