【設(shè)計(jì)模式筆記】(零)- 設(shè)計(jì)模式六大原則

1.單一職責(zé)原則(Single Responsibility Principle,縮寫(xiě)SRP)

單一職責(zé)原則,就一個(gè)類(lèi)而言,應(yīng)該只有一個(gè)引起它變化的原因。簡(jiǎn)單說(shuō),一個(gè)類(lèi)應(yīng)該是一組高度相關(guān)的函數(shù)、數(shù)據(jù)的封裝;也就是高內(nèi)聚。

下面代碼為 ImageLoader(圖片加載)類(lèi)的代碼

public class ImageLoader{
    //圖片緩存
    LruCache<String,Bitmap> mImageCache;
    //線程池,線程數(shù)量為CPU的數(shù)量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProessors());
    
    public ImageLoader(){
        initImageCache();
    }
    
    private void initImageCache() {
        //省略...         
   }
    
   //顯示圖片
   public  void displayImage(final String url, final ImageView imageView) {
        //省略... 
   }
    
   //下載圖片
   public  Bitmap downloadImage(String imageUrl) {
        //省略... 
        return bitmap;
   }
}

這里可以看出來(lái) ImageLoader 類(lèi)作用有初始化圖片緩存、顯示圖片、下載圖片,顯然顯示圖片和下載圖片兩個(gè)方法與初始化圖片緩存方法相比作用就顯得有些不相關(guān)。也就是不符合單一職責(zé)原則。按照邏輯進(jìn)行分拆之后得到ImageLoaderImageCache兩個(gè)類(lèi)。ImageLoader負(fù)責(zé)圖片加載邏輯,ImageCache負(fù)責(zé)處理圖片緩存邏輯,這樣職責(zé)就清楚了,當(dāng)與緩存相關(guān)的邏輯需要改變時(shí),不需要修改ImageLoader類(lèi),而圖片加載的邏輯需要修改時(shí)也不會(huì)影響到緩存處理邏輯。

image

ImageLoader代碼修改如下所示:

/** 圖片加載類(lèi) */
public  class ImageLoader {
    //圖片緩存
    ImageCache mImageCache = new ImageCache() ;
    //線程池,線程數(shù)量為CPU的數(shù)量
    ExecutorService mExecutorService = Executors.newFixedThreadPool (Runtime.getRuntime().availableProcessors());

    //加載圖片
    public  void displayImage(final String url, final ImageView imageView) {
        //省略... 
     }

    public  Bitmap downloadImage(String imageUrl) {
        //省略... 
        return bitmap;
    }
} 

而添加的ImageCache類(lèi)用于處理圖片緩存,具體代碼如下:

public class ImageCache {
    // 圖片LRU緩存
    LruCache<String, Bitmap> mImageCache;

    public ImageCache() {
        initImageCache();
    }

    private void initImageCache() {
        //省略... 
    }

    public void put(String url, Bitmap bitmap) {
        mImageCache.put(url, bitmap) ;
    }

    public Bitmap get(String url) {
        return mImageCache.get(url) ;
    }
}

如何劃分一個(gè)類(lèi)、一個(gè)函數(shù)的職責(zé),每個(gè)人都有自己的看法,這需要根據(jù)個(gè)人經(jīng)驗(yàn)、具體的業(yè)務(wù)邏輯而定。

2.開(kāi)閉原則(Open Close Principle,縮寫(xiě)OCP)

開(kāi)閉原則是Java中最基礎(chǔ)的設(shè)計(jì)原則,知道我們?nèi)绾谓⒁粋€(gè)穩(wěn)定的、靈活的系統(tǒng)。定義:軟件中得對(duì)象應(yīng)該對(duì)于擴(kuò)展是開(kāi)放的,但是對(duì)于修改是封閉的。

例如圖中MemonyCache、DiskCache、DoubleCache都實(shí)現(xiàn)了ImageCache接口,ImageLoader使用ImageCache處理緩存,就意味著ImageLoader可以通過(guò)setImageCache()指定使用哪一種緩存類(lèi)型,可以使三種緩存其中任意一種,同時(shí)不需要修改ImageLoader中的代碼。這也就是開(kāi)閉原則的體現(xiàn)。

簡(jiǎn)單地說(shuō),當(dāng)軟件需要變化時(shí),應(yīng)該盡量通過(guò)擴(kuò)展的方式來(lái)實(shí)現(xiàn)變化,而不是通過(guò)修改已有的代碼來(lái)實(shí)現(xiàn)?!皯?yīng)該盡量”4個(gè)字說(shuō)明OCP原則并不是說(shuō)絕對(duì)不可以修改原始類(lèi)的,當(dāng)代碼需要需要重構(gòu)的時(shí)候要及時(shí)重構(gòu),使代碼恢復(fù)正常,而不是通過(guò)繼承等方式添加新的實(shí)現(xiàn),這會(huì)導(dǎo)致類(lèi)型的膨脹以及歷史遺留代碼的冗余。

開(kāi)發(fā)過(guò)程中都沒(méi)有那么理想的狀況,因此,凡事也是需要結(jié)合具體情況再做決定,目的是更穩(wěn)定、更靈活同時(shí)保有原有的正確性。

3.里氏替換原則(Liskov Substitution Principle,縮寫(xiě)LSP)

里氏替換原則,書(shū)上原話的定義簡(jiǎn)直看不得(解釋的辣眼睛,完全看不懂),簡(jiǎn)單地說(shuō)就是所有引用基類(lèi)的地方必須能透明地使用其子類(lèi)的對(duì)象。只要父類(lèi)能出現(xiàn)的地方子類(lèi)就可以出現(xiàn),而且替換為子類(lèi)也不會(huì)產(chǎn)生任何錯(cuò)誤或異常,使用者可能根本就不需要知道是父類(lèi)還是子類(lèi)。但是,反過(guò)來(lái)就不行了,有子類(lèi)出現(xiàn)的地方,父類(lèi)未必就能適應(yīng)。其實(shí)就是:抽象。

上圖可以看出,Window依賴(lài)于View,而ButtonTextView繼承View。這里任何繼承自View類(lèi)的子類(lèi)都可以設(shè)置給show()方法,也就是里氏替換原則。通過(guò)里氏替換,就可以自定義各式各樣的View,然后傳遞給Window,并且將View顯示到屏幕上。

里氏替換原則的核心原理是抽象,抽象又依賴(lài)于繼承這個(gè)特性,繼承的優(yōu)缺點(diǎn)都相當(dāng)明

優(yōu)點(diǎn):

  • 代碼重用,減少創(chuàng)建類(lèi)的成本,每個(gè)子類(lèi)都擁有父類(lèi)的方法和屬性
  • 子類(lèi)與父類(lèi)基本相似,但又與父類(lèi)有所區(qū)別
  • 提高代碼的可擴(kuò)展性

缺點(diǎn):

  • 繼承是侵入性的,只要繼承就必須擁有父類(lèi)的所有屬性和方法
  • 可能造成子類(lèi)代碼冗余、靈活性降低,因?yàn)樽宇?lèi)必須擁有父類(lèi)的屬性和方法

事物總是具有兩面性,如何權(quán)衡利與弊都是需要根據(jù)具體場(chǎng)景來(lái)做出選擇并加以處理。

4.依賴(lài)倒置原則(Dependence Inversion Principle,縮寫(xiě)DIP)

依賴(lài)倒置原則,說(shuō)的就是一種特定的就形式,使得高層次的模塊不依賴(lài)于低層次的模塊的實(shí)現(xiàn)細(xì)節(jié)的目的,依賴(lài)模塊被顛倒了。依賴(lài)倒置原則的幾個(gè)關(guān)鍵點(diǎn):

  • 高層模塊不應(yīng)該依賴(lài)低層模塊,兩者都應(yīng)該依賴(lài)其抽象
  • 抽象不應(yīng)該依賴(lài)細(xì)節(jié)
  • 細(xì)節(jié)應(yīng)該依賴(lài)抽象

是不是覺(jué)得和沒(méi)說(shuō)一個(gè)樣,至少我是這么覺(jué)得的;繼續(xù)往后看才明白,所謂高層模塊就是調(diào)用端,低層模塊就是具體實(shí)現(xiàn)類(lèi)。依賴(lài)倒置原則在 Java 語(yǔ)言中的表現(xiàn)就是:模塊間的依賴(lài)通過(guò)抽象發(fā)生,實(shí)現(xiàn)類(lèi)之間不發(fā)生直接的依賴(lài)關(guān)系,通過(guò)接口抽象類(lèi)產(chǎn)生依賴(lài)關(guān)系。什么是依賴(lài)關(guān)系呢?其實(shí)就是相互之間的調(diào)用關(guān)系。概括來(lái)說(shuō)就是面向接口變成,或者是面向抽象編程。

其實(shí)依賴(lài)倒置原則主要目的就是解耦

依然可以使用這張圖來(lái)表示,表達(dá)出來(lái)就是ImageLoaderMemonyCache等并沒(méi)有直接關(guān)系,甚至ImageLoader只需要實(shí)現(xiàn)ImageCache類(lèi)或繼承其他已有的ImageCache子類(lèi)完成相應(yīng)的緩存功能,然后將具體的實(shí)現(xiàn)注入到ImageLoader即可實(shí)現(xiàn)緩存功能的替換。這也是依賴(lài)倒置原則的體現(xiàn)。

5.接口隔離原則(Interface Segregation Principle,縮寫(xiě)ISP)

接口隔離原則將非常龐大、臃腫的接口拆分成為更小的和更具體的接口;目的就是解耦。這個(gè)原則的做法和單一職責(zé)原則有點(diǎn)相似,就是說(shuō)接口中得方法保持更高的相關(guān)性、盡量少,避免掉不需要的方法。

舉個(gè)栗子,現(xiàn)在有一個(gè)帶有呼吸方法的接口,還有一個(gè)打鼾方法的接口;如果說(shuō),你把這兩個(gè)方法放到一個(gè)接口中,基本就是違背接口隔離原則,畢竟呼吸和打鼾沒(méi)有什么緊密的相關(guān)性;不可能說(shuō)我需要呼吸的時(shí)候一定需要打鼾吧!

6.迪米特原則(Law of Principle,縮寫(xiě)LOP)

迪米特原則也稱(chēng)為最少知識(shí)原則(Least Knowledge Principle),定義:一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象有最少的了解。通俗地講,一個(gè)類(lèi)要對(duì)自己需要調(diào)用的類(lèi)知道得最少,類(lèi)的內(nèi)部如何實(shí)現(xiàn)、如何復(fù)雜都與調(diào)用者(或依賴(lài)者)沒(méi)關(guān)系,調(diào)用者(或依賴(lài)者)只需要知道他需要的方法即可,其他的不需要關(guān)心。類(lèi)與類(lèi)之間的關(guān)系越密切,耦合度越大;當(dāng)一個(gè)類(lèi)發(fā)生改變時(shí),對(duì)另一個(gè)類(lèi)的影響也越大。

迪米特法則還有一個(gè)英文解釋是:Only talk to your immedate friends,翻譯過(guò)來(lái)就是:只與直接的朋友通信。什么叫做直接的朋友呢?每個(gè)對(duì)象都必然會(huì)與其他對(duì)象有耦合關(guān)系,兩個(gè)對(duì)象之間的耦合就成為朋友關(guān)系,這種關(guān)系的類(lèi)型有很多,例如組合、聚合、依賴(lài)等。

下圖是Volley框架中的DiskBasedCache類(lèi)(實(shí)現(xiàn)Cache接口)和Cache接口的代碼

Volley中的Response緩存接口的設(shè)計(jì)。Cache接口定義了緩存類(lèi)需要實(shí)現(xiàn)的最小接口,依賴(lài)緩存類(lèi)的對(duì)象只需要知道這些接口即可。例如緩存的具體實(shí)現(xiàn)類(lèi)DiskBasedCache,該緩存類(lèi)將Response序列化到本地,這就需要操作File以及相關(guān)的類(lèi)。

Volley的直接朋友就是DiskBasedCache,間接朋友就是mRootDirectory、mEntries等。Volley只需要直接和Cache類(lèi)交互即可,并不需要知道File、mEntries等對(duì)象的存在。

文中有引用書(shū)本中得例子,也有根據(jù)自己理解舉的例子,如有不對(duì)還望指出。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容