Android 設計模式之面向對象的六大原則

在日常開發過程中時常需要用到設計模式,但是設計模式有23種,如何將這些設計模式了然于胸并且能在實際開發過程中應用得得心應手呢?和我一起跟著《Android源碼設計模式解析與實戰》一書邊學邊應用吧!


今天我們要講的是面向對象的六大原則

單一職責原則

就一個類而言,應該僅有一個引起它變化的原因。簡單來說,一個類中應該是一組相關性很高的函數、數據的封裝。
  • 我們在App中往往會用到很多的公共方法,比如獲取系統的時間,這個功能可能在App中的很多地方都要用到。這個時候我們一般會單獨寫個工具類TimeUtils,把處理時間有關的方法都放到這個類里面,這樣就能減少重復代碼,App的結構會更加清晰。當需要添加其他跟時間有關的方法時,就可以都加到這個TimeUtils類里面,這就是我們平時遵循的單一職責原則。

開閉原則

軟件中的對象(類、模塊、函數等),應該對于擴展是開放的,而對于修改是封閉的。
  • 我們在軟件開發過程中就要考慮到后續的擴展和修改。比如說,我們在開發一款類似于universal-image-loader的圖片加載框架,可能一開始我們的功能比較簡單,圖片緩存只有內存緩存。當我們新版本需要添加SD卡緩存時,就要注意盡可能的減少對原來代碼的修改,因為這樣很可能會引入新的bug。而要做到開閉原則,一般有2種途徑,一是通過繼承原有的類;二是通過抽象和接口。后面我們會拿書中的圖片加載框架來具體說明。

里氏替換原則

所有引用基類的地方必須能透明的使用其子類。通俗的說,就是只要父類能出現的地方子類就可以出現,而且替換為子類以后不會出現任何錯誤或異常。反過來就不行了,子類出現的地方父類不一定能適應。
  • 要實現里氏替換原則,一般需要一個抽象的父類,父類中定義了子類的公共方法,子類繼承或是實現父類以后擴展不同的功能,這樣以來可以實現根據不同的需要來應用對應的子類,從而達到應用不同的功能的目的,程序的擴展性大大增強。同時這也體現了開閉原則,即當需要增加新功能時,只要繼承或實現父類,實現新增的功能就達到了擴展的目的,而不是直接修改原來的代碼,也即對擴展開放,對修改封閉。

依賴倒置原則

依賴倒置原則在Java中的表現就是:模塊間的依賴通過抽象發生,實現類之間不發生直接的依賴關系,其依賴關系是通過接口或抽象類產生的。
  • 一句話就是依賴抽象而不依賴具體的實現。比如上面我們說開發一個圖片加載框架,那我們肯定會根據單一職責原則劃分不同的模塊,比如網絡加載模塊,圖片緩存模塊,以及主模塊等。主模塊肯定會調用圖片緩存模塊,如果我們調用的是圖片緩存模塊的具體實現,那么當我們修改圖片模塊時就很可能要對應修改主模塊,這就是耦合了。一個比較好的做法是將圖片緩存模塊抽象出來,而主模塊調用這個抽象即可,這樣也就是依賴抽象了。

接口隔離原則

類間的依賴關系應該建立在最小的接口上。
  • 接口隔離原則就是讓客戶端依賴的接口盡可能的小。就是在上面提到的依賴倒置(依賴抽象而不是實現)原則的基礎上,增加一個最小化依賴的原則。說白了就是在依賴接口的基礎上依賴盡可能少的接口。
  • 這里舉個例子:
<!--將圖片寫入SD卡-->
public void put(String url, Bitmap bitmap) {
    FileOutputStream fileOutputStream = null;
    try {
        fileOutputStream = new FileOutputStream(SDPath+url);
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
    }catch (FileNotFoundException e) {
        e.printStackTrace();
    }finally {
        CloseUtils.closeQuietly(fileOutputStream);
    }
}

<!--關閉工具類-->
public final class CloseUtils {

    private CloseUtils() { }

    /**
     * 關閉Closeable對象
     * @param closeable
     */
    public static void closeQuietly(Closeable closeable) {
        if (null != closeable) {
            try {
                closeable.close();
            }catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 上述例子中的CloseUtils的closeQuietly方法的原理就是依賴于Closeable抽象而不是具體實現(依賴倒置原則),并且建立在最小化依賴的基礎上,它只要知道這個對象是可以關閉的就行了,其他的一概不用關心,這就是接口隔離原則

迪米特原則

一個對象應該對其他的對象有最少的了解
  • 通俗的講,一個類應該對自己需要耦合或調用的類知道得最少,調用者或是依賴者只要知道它需要的方法即可。要做到這個原則,需要我們對各個模塊之間的功能進行很好的區分和分配,把相互之間的依賴和耦合減到最少。

以上就是面向對象的六大原則。

下面我們通過書中的圖片加載框架ImageLoader的例子介紹這些原則的具體應用。
  • 首先是ImageLoader類
public class ImageLoader {
    // 圖片緩存,依賴接口,而不是具體實現
    // 如果改為MemoryCache mImageCache = new MemoryCache();就不能定制圖片緩存的實現,擴展性大大降低,耦合度也會大大提高
    ImageCache mImageCache = new MemoryCache();
    // 線程池,線程數量為CPU的數量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    // 注入緩存,對擴展開放,對修改關閉
    public void setImageCache(ImageCache cache) {
        mImageCache = cache;
    }

    /**
     * 顯示圖片
     * @param imageUrl
     * @param imageView
     */
    public void displayImage(String imageUrl, ImageView imageView) {
        Bitmap bitmap = mImageCache.get(imageUrl);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }
        // 圖片沒有緩存,提交到線程池下載
        submitLoadRequest(imageUrl, imageView);
    }

    /**
     * 下載圖片
     * @param imageUrl
     * @param imageView
     */
    private void submitLoadRequest(final String imageUrl, final ImageView imageView) {
        imageView.setTag(imageUrl);
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = downloadImage(imageUrl);
                if (bitmap == null) {
                    return;
                }
                if (imageUrl.equals(imageView.getTag())) {
                    imageView.setImageBitmap(bitmap);
                }
                mImageCache.put(imageUrl, bitmap);
            }
        });
    }

    /**
     * 下載圖片
     * @param imageUrl
     * @return
     */
    private Bitmap downloadImage(String imageUrl) {
        Bitmap bitmap = null;
        try {
            URL url = new URL(imageUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            bitmap = BitmapFactory.decodeStream(connection.getInputStream());
            connection.disconnect();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }
  • 然后是圖片緩存接口以及內存緩存
<!--圖片緩存接口-->
public interface ImageCache {
    public Bitmap get(String url);
    public void put(String url, Bitmap bitmap);
}

<!--內存緩存的實現-->
public class MemoryCache implements ImageCache{
    private LruCache<String, Bitmap> mMemoryCache;

    public MemoryCache() {
        //初始化LRU緩存
        initImageCache();
    }
    
    private void initImageCache() {
        // 計算可使用的最大內存
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory()/1024);
        // 取四分之一的可用內存作為緩存
        final int cacheSize = maxMemory / 4;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };
    }


    @Override
    public Bitmap get(String url) {
        return mMemoryCache.get(url);
    }

    @Override
    public void put(String url, Bitmap bitmap) {
        mMemoryCache.put(url, bitmap);
    }
}
ImageLoader類中的邏輯比較直觀,六大原則的體現主要在圖片緩存的處理上。
  • 將圖片緩存單獨出去而不是寫在ImageLoader一起,體現了單一職責原則
  • ImageLoader中依賴的是圖片緩存的接口而不是具體的實現,體現了開閉原則、里氏替換原則和依賴倒置原則。
  • ImageLoader類和MemoryCache類之間只依賴ImageCache接口,也可以說體現了接口隔離原則
  • ImageLoader類只需要知道MemoryCache類的put方法和get方法,其他的實現一概不管,也體現了迪米特原則
當然以上的ImageLoader還只是初版,還有很多有待優化的地方,比如可以把下載的邏輯單獨出去,可以增加更多的定制功能等。

六大原則能讓我們的代碼結構更加合理,擴展性更好,讓程序更穩定靈活。這些原則往往都是共同作用的,比如上面例子中的CloseUtils滿足了單一職責原則,closeQuietly方法運用了依賴倒置,并且遵循接口隔離原則。趕緊用六大原則優化你的代碼吧!!!
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容