《Android原始碼設計模式解析與實戰》讀書筆記

揚州慢發表於2017-12-17

物件導向的六大原則

單一職責原則

Single Responsibility Principle(SRP),定義是:就一個類而言,應該僅有一個引起它變化的原因。簡單來說,一個類中應該使一組相關性很高的函式、資料的封裝。

例如一個ImageLoader實現圖片載入,並要將圖片快取起來。

  • 有的人可能會這樣實現:是直接在一個類裡實現圖片下載、LruCache、displayImage等。但這樣耦合太嚴重,毫無擴充套件性。隨著功能增加,類會越來越大,系統越來越脆弱。

  • 較好的實現是對ImageLoader進行拆分,新增一個類ImageCache類用於處理圖片快取。ImageLoader只負責圖片載入的邏輯。

開閉原則

Open Close Principle(OCP),定義是:軟體中的物件(類、模組、函式等)應該對於擴充套件是開放的,但是,對於修改是封閉的。在軟體的生命週期內,因為變化、升級和維護等原因需要對軟體進行原有程式碼修改時,可能會將錯誤引入原本已經過測試的舊程式碼中,破壞原有系統。因此,當軟體需要變化時,我們應該儘量通過擴充套件的方式來實現變化,而不是通過修改已有的程式碼來實現。

《物件導向軟體構造》中提出了開閉原則。這一想法認為,程式一旦開發完成,程式中一個類的實現只應該因錯誤而被修改,新的或者改變的特性應該通過新建不同的類而實現,新建的類可通過繼承的方式來重用原類的程式碼。

以ImageLoader為例,需要實現記憶體快取、SD卡快取以及雙快取:

  • 有的人可能會這樣實現: ImageCache裡持有記憶體快取類ImageCache、SD卡快取類DiskCache,再通過設定布林值來使用if-else判斷語句來確定使用哪種快取。隨著邏輯的引入,程式碼會變得越來越脆弱、複雜,如果一不小心寫錯某個if條件,就需要更多的時間來排除。最重要的是,使用者不能自己實現快取注入到ImageLoader中,可擴充套件性差,可擴充套件性是框架的最重要特性之一。

  • 較好的實現是:提取一個圖片快取的介面,用來抽象圖片快取的功能:

    public interface ImageCache {
        public Bitmap get(String url);
        public void put(String url, Bitmap bmp);
    }
    複製程式碼

    記憶體快取、SD卡快取、雙快取都實現了該介面。ImageLoader類中增加了一個setImageCache(ImageCache cache)函式,使用者可以通過該函式設定快取實現,也就是通常說的依賴注入。通過函式注入不同的快取實現,這樣不僅是ImageLoader更簡單、健壯,也是可擴充套件性、靈活性更高。幾種快取的具體實現不一樣,但是,它們有個特點是都實現了ImageCache介面。當使用者需要自定義實現快取策略時,只需要新建一個實現ImageLoader介面的類,然後改造該類的物件,並通過設定函式注入到ImageLoader中,這樣就實現了千變萬化的快取策略。

里氏替換原則

全稱是Liskov Substitution Principle(LSP)。定義是:如果對每一個型別為S的物件O1,都有型別為T的物件O2,使得以T定義的所有程式P在所有的O1都代換為O2時,程式P的行為沒有發生變化,那麼型別S是型別T的子型別。

里氏替換原則第二種定義:所有引用基類的地方必須能透明地使用其之類的物件。通俗點講,只要父類能出現的地方子類就可以出現,而且替換為子類也不會產生任何錯誤或異常,使用者可能根本就不需要知道是父類還是子類。但是,反過來就不行,有子類出現的地方,父類未必就能適應。最終總結就兩個字:抽象。

在OOP中(物件導向程式設計,Object Oriented Programming,OOP),繼承的優缺點都相當明顯。

優點:

  1. 程式碼重用,減少建立類的成本,每個子類都擁有父類的方法和屬性;
  2. 子類與父類基本相似,但又與父類有所區別;
  3. 提高程式碼的可擴充套件性。

繼承的缺點:

  1. 繼承是侵入性的,只要繼承就必須擁有父類的所有屬性和方法;
  2. 可能造成子類程式碼冗餘、靈活性降低,因為子類必須擁有父類的所有屬性和方法。

里氏替換原則就是為這類問題提供指導原則,也就是建立抽象。通過抽象建立規範,具體的實現在與形式替換掉抽象,保證系統的擴充套件性、靈活性。開閉原則和里氏替換原則往往是生死相依的,通過里氏替換來達到對擴充套件開放,對修改關閉的效果。兩個原則都同時強調了OOP的一個重要特性——抽象。

依賴倒置原則

Dependence Inversion Principle(DIP)。依賴倒置原則指代了一種特定的解耦形式,使得高層次的模組不依賴於低層次的模組的實現細節的目的,依賴模組被顛倒了。其有幾個關鍵點:

  1. 高層模組不應該依賴低層模組,兩者都應該依賴其抽象;
  2. 抽象不應該依賴細節;
  3. 細節應該依賴抽象。

JAVA中,抽象就是指介面或抽象類;細節就是實現類;高層模組就是呼叫端,低層模組就是具體實現類。依賴倒置在JAVA語言中的表現是:模組間的依賴通過抽象發生,實現類之間不發生直接的依賴關係,其依賴關係是通過介面或抽象類產生的。一句話概括:面向介面(抽象)程式設計。

如果類與類直接依賴於細節,那麼它們之間就有直接的耦合,當具體實現需要變化時,意味著要同時修改依賴者的程式碼,這限制了系統的可擴充套件性。例如之前的例子,ImageLoader直接依賴於MemoryCache,這個MemoryCache是一個具體實現而不是抽象,這導致了ImageLoader直接依賴了具體細節,當MemoryCache不能滿足ImageLoader而需要被其他快取實現替代時,就必須修改ImageLoader。

介面隔離原則

InterfaceSegregation Principles(ISP)。定義是:客戶端不應該依賴它不需要的介面。介面隔離原則的目的是系統解開耦合,從而容易重構、更改和重新部署。說白了就是讓客戶端依賴的介面儘可能地小。

例如ImageLoader中的ImageCache就是介面隔離原則的運用,ImageLoader只需要知道該快取物件有存、取快取圖片的介面即可,其他的一概不管,這就使得快取功能的具體實現對ImageLoader隱藏,用最小化介面隔離了實現類的細節,也促使我們將龐大的介面拆分到更細粒度的介面當中,使系統具有更低的耦合性,更高的靈活性。

迪米特原則(最少只是原則)

Law of Demeter(LOD)。一個物件應該對其他物件有最少的瞭解。還有一種解釋是:只與直接的朋友通訊。

例如,租房子,中介擁有房源,客戶對租金和麵積有要求。

  • 有的人可能會這樣實現:Room有屬性area、price,Mediator(中介)有屬性List,Tenant(客戶)有屬性myPrice、myArea,有方法isSuitable(Room room)來判斷房源是否符合要求。

    耦合太嚴重

    Tenant需要Mediator遍歷擁有的房源,然後再對Room進行比較是否符合條件。

  • 較好的實現:

    解耦

將對於Room的判定操作移到Mediator類中,這本應該是中介的職責,根絕使用者的條件查詢防止,使用者只需要我們的朋友——中介溝通就好了。“只與直接的朋友通訊。”

單例模式

定義:確保某一個類只有一個例項,而且自行例項化並向整個系統提供這個例項。

一般有如下幾個關鍵點:

  1. 建構函式不對外開放,一般為Private;
  2. 通過一個靜態方法或者列舉返回單例類物件;
  3. 確保單例類的物件有且只有一個,尤其是在多執行緒環境下;
  4. 確保單例類物件在反序列化時不會重新構建物件。
public class Singleton {
        private static Singleton instance;

        private Singleton() {}

        public static synchronized Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
複製程式碼

建造者模式

定義

將一個複雜物件的構建與它的表示分離,使得同樣的構建過程建立不同的表示

使用場景

  • 相同的方法,不同的執行順序,產生不同的事件結果
  • 多個部件或零件,都可以裝配到一個物件中,但產生的執行結果又不相同
  • 產品類非常複雜,或者產品類中的呼叫順序不同產生不同的作用
  • 初始化一個物件特別複雜,如引數多,且很多引數都有預設值

構成

相關文章