面向對象原則之開閉原則

1.簡稱

開閉原則的全稱是Open close Principle ,縮寫是OCP

2.定義

軟件中的對象(類龄句、模塊傀蚌、函數(shù)等)應該 對于擴展是開放的勋桶,對于修改是封閉的脱衙。

3.問題

在軟件的生命周期內,因為變化例驹、升有代碼進級和維護等原因需要對軟件原行修改時捐韩,可能會將錯誤引入原本已經(jīng)測試過的舊版本中,破環(huán)原有系統(tǒng)鹃锈。

4.解決

盡量使用擴展的方式實現(xiàn)變化荤胁,但在實際開發(fā)中往往修改原有代碼、擴展代碼同時進行仪召。
舉例:
內存緩存

public class ImageCache {
    LruCache<String,Bitmap> mImageCache;//圖片LRU緩存
    public ImageCache(){
        initImageCache();
    }
    private void initImageCache() {
        final int maxMemory =(int)(Runtime.getRuntime().maxMemory()/1024);
        final int cacheSize =maxMemory /4;
        mImageCache =new LruCache<String, Bitmap>(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes()* bitmap.getHeight() /1024;
            }
        };
    }
    public void put(String url,Bitmap bitmap){
        mImageCache.put(url,bitmap);
    }
    public Bitmap get(String url){
        return mImageCache.get(url);
    }
}

SD卡緩存

public class DiskCache {
    static String cacheDir ="sdcard/cache/";
    //從緩存中獲取圖片
    public Bitmap get(String url){
        return BitmapFactory.decodeFile(cacheDir+ url);
    }
    public void put (String url,Bitmap bitmap){
        FileOutputStream fileOutputStream =null;
        try {
            fileOutputStream =new FileOutputStream(cacheDir + url);
            bitmap.compress(Bitmap.CompressFormat.PNG,100,fileOutputStream);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(fileOutputStream!=null){
                try {
                    fileOutputStream.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

雙緩存

public class DoubleCache {
    ImageCache mMemoryCache = new ImageCache();
    DiskCache mDiskCache =new DiskCache();
  
    public void put(String url,Bitmap bitmap){
        mMemoryCache.put(url,bitmap);
        mDiskCache.put(url,bitmap);
    }

    //先從內存緩存中獲取圖片寨蹋,如果沒有,再從SD卡中獲取
    public Bitmap get(String url){
        Bitmap bitmap =mMemoryCache.get(url);
        if(bitmap==null){
              bitmap =mDiskCache.get(url);
        }
        return bitmap;
    }
      
}

ImageLoader類

public class ImageLoader {
    ImageCache mImageCache =new ImageCache();//圖片緩存
    DiskCache mDiskCache =new DiskCache();//sd卡緩存
    DoubleCache mDoubleCache =new DoubleCache();//雙緩存
    boolean isUseDiskCache =false;//是否使用SD卡緩存
    boolean isUseDoubleCache =false;//是否使用雙緩存
    //線程池 線程數(shù)量為CPU的數(shù)量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    private void displayImage(final String url, final ImageView imageView){
        Bitmap bitmap= null;
        if(isUseDoubleCache){
            bitmap =mDoubleCache.get(url);
        }else if(isUseDiskCache){
            bitmap =mDiskCache.get(url);
        }else {
            bitmap =mImageCache.get(url);
        }
        if(bitmap!=null){
            imageView.setImageBitmap(bitmap);
            return;
        }
        imageView.setTag(url);
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap =downloadImage(url);
                if(bitmap ==null){
                    return;
                }
                if(imageView.getTag().equals(url)){
                    imageView.setImageBitmap(bitmap);
                }
                mImageCache.put(url,bitmap);
            }
        });
    }
    public Bitmap downloadImage(String imageUrl){
        Bitmap bitmap =null;
        try{
            URL url =new URL(imageUrl);
            final HttpURLConnection conn =(HttpURLConnection)url.openConnection();
            bitmap = BitmapFactory.decodeStream(conn.getInputStream());
            conn.disconnect();
        }catch (Exception e){
            e.printStackTrace();
        }
        return bitmap;
    }
    public void useDiskCache(boolean useDiskCache){
        isUseDiskCache =useDiskCache;
    }
    public void useDoubleCache(boolean useDoubleCache){
        isUseDoubleCache =useDoubleCache;
    }
}

上面的代碼可以自由控制使用內存緩存扔茅,SD卡緩存已旧,雙緩存,看似挺好的召娜,但是有一個問題运褪,每次在程序中加入新的緩存實現(xiàn)時都需要修改ImageLoader類,然后通過布爾值讓用戶選擇使用哪種緩存玖瘸,因此在ImageLoader中存在各種if-else判斷語句秸讹,通過這些判斷來確定使用哪些緩存。隨著邏輯的引入雅倒,代碼變得越來越復雜璃诀,某一個if條件寫錯就要花很長時間排除。另外用戶不能自己實現(xiàn)緩存注入到ImageLoader中蔑匣,可擴展性差劣欢。

怎么修改呢棕诵?
可以通過定義一個接口,里面為公用方法凿将,然后讓所有緩存對象實現(xiàn)這個接口校套,在ImageLoader中定義一個方法setImageCache,用到哪個緩存類就傳入哪個緩存類牧抵,自由度高笛匙。

public class ImageLoader {
    ImageCache mImageCache = new MemoryCache();//圖片緩存
    //注入緩存實現(xiàn)
    public void setImageCache(ImageCache cache) {
        mImageCache = cache;
    }
    //線程池 線程數(shù)量為CPU的數(shù)量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    private void displayImage(final String url, final ImageView imageView) {
        Bitmap bitmap = mImageCache.get(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }
        submitLoadRequest(url, imageView);
    }
    /**
     * 緩存中沒圖片 則從網(wǎng)絡下載
     *
     * @param url
     * @param imageView
     */
    private void submitLoadRequest(final String url, final ImageView imageView) {
        imageView.setTag(url);
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = downloadImage(url);
                if (bitmap == null) {
                    return;
                }
                if (imageView.getTag().equals(url)) {
                    imageView.setImageBitmap(bitmap);
                }
                mImageCache.put(url, bitmap);
            }
        });
    }
    public Bitmap downloadImage(String imageUrl) {
        Bitmap bitmap = null;
        try {
            URL url = new URL(imageUrl);
            final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            bitmap = BitmapFactory.decodeStream(conn.getInputStream());
            conn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }
}

注意這里的ImageCache是一個接口,并不是前面的類犀变,主要用來抽象圖片緩存的功能妹孙。緩存的key是圖片的url.值是圖片的本身。

public interface ImageCache {
    public Bitmap get(String url);
    public void put(String url,Bitmap bitmap);
}

內存緩存获枝,sd卡緩存涕蜂,雙緩存都實現(xiàn)了該接口。

//內存緩存類
public class MemoryCache implements ImageCache{
    private LruCache<String,Bitmap> mMemoryCache;
    public  MemoryCache(){
        initImageCache();
    }
    @Override
    public Bitmap get(String url) {
        return mMemoryCache.get(url);
    }
    @Override
    public void put(String url, Bitmap bitmap) {
        mMemoryCache.put(url,bitmap);
    }
    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;
            }
        };
    }
}
//sd卡緩存
public class DiskCache implements ImageCache{
    static String cacheDir ="sdcard/cache/";
    //從緩存中獲取圖片
    public Bitmap get(String url){
        return BitmapFactory.decodeFile(cacheDir+ url);
    }
    public void put (String url,Bitmap bitmap){
        FileOutputStream fileOutputStream =null;
        try {
            fileOutputStream =new FileOutputStream(cacheDir + url);
            bitmap.compress(Bitmap.CompressFormat.PNG,100,fileOutputStream);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(fileOutputStream!=null){
                try {
                    fileOutputStream.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

//雙緩存
public class DoubleCache implements ImageCache{
    ImageCache mMemoryCache = new MemoryCache();
    ImageCache mDiskCache =new DiskCache();
    //先從內存緩存中獲取圖片映琳,如果沒有,再從SD卡中獲取
    public Bitmap get(String url){
        Bitmap bitmap =mMemoryCache.get(url);
        if(bitmap==null){
            bitmap =mDiskCache.get(url);
        }
        return bitmap;
    }
    public void put(String url,Bitmap bitmap){
        mMemoryCache.put(url,bitmap);
        mDiskCache.put(url,bitmap);
    }
}

那么怎樣設置實現(xiàn)緩存呢蜘拉?

ImageLoader imageLoader =new ImageLoader();
//使用內存緩存
imageLoader.setImageCache(new MemoryCache());
//使用sd卡緩存
imageLoader.setImageCache(new DiskCache());
//使用雙緩存
imageLoader.setImageCache(new DoubleCache());
//使用自定義的圖片緩存
imageLoader.setImageCache(new ImageCache(){
   @Override
   public void put(String url,Bitmap bitmap){
        //緩存圖片
   }
   @Override
   public Bitmap get(String url){
     //從緩存中獲取圖片
     return null;
   }
});

通過setImageCache(ImageCache cache) 方法注入不同的緩存實現(xiàn)萨西,這樣能夠使ImageLoader更簡單,健壯旭旭,也使得ImageLoader的擴展性谎脯、靈活性更高。MemoryCache持寄、DiskCache源梭、DoubleCache緩存圖片的具體實現(xiàn)完全不一樣,但是稍味,它們都實現(xiàn)了ImageCache接口废麻。當用戶需要自定義緩存策略時,只需要新建一個實現(xiàn)ImageCache的接口的類模庐,然后構造該類的對象烛愧,并且通過setImageCache(ImageCache cache)注入到ImageLoader中,這樣ImageLoader就實現(xiàn)了千變萬化的緩存策略掂碱,且擴展這些緩存策略并不會導致ImageLoader類的修改怜姿。

總結

當需求發(fā)生變化時,應該盡量通過擴展的方式來實現(xiàn)變化疼燥,而不是修改原有代碼來實現(xiàn)沧卢,盡量遵循開閉原則。
開閉原則的優(yōu)點:
1.通過擴展已有的軟件系統(tǒng)醉者,可以提供新的行為但狭,以滿足對軟件的新需求披诗,使變化中的軟件系統(tǒng)有一定的適應性和靈活性。
2.已有的軟件模塊熟空,特別是最重要的抽象層模塊不能再修改藤巢,這就使變化中的軟件系統(tǒng)有一定的穩(wěn)定性和延續(xù)性。
3.增加復用性和可維護性息罗。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末掂咒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子迈喉,更是在濱河造成了極大的恐慌绍刮,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挨摸,死亡現(xiàn)場離奇詭異孩革,居然都是意外死亡,警方通過查閱死者的電腦和手機得运,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門膝蜈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人熔掺,你說我怎么就攤上這事饱搏。” “怎么了置逻?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵推沸,是天一觀的道長。 經(jīng)常有香客問我券坞,道長鬓催,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任恨锚,我火速辦了婚禮宇驾,結果婚禮上,老公的妹妹穿的比我還像新娘猴伶。我一直安慰自己飞苇,他們只是感情好,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布蜗顽。 她就那樣靜靜地躺著布卡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪雇盖。 梳的紋絲不亂的頭發(fā)上忿等,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機與錄音崔挖,去河邊找鬼贸街。 笑死庵寞,一個胖子當著我的面吹牛,可吹牛的內容都是我干的薛匪。 我是一名探鬼主播捐川,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逸尖!你這毒婦竟也來了古沥?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤娇跟,失蹤者是張志新(化名)和其女友劉穎岩齿,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體苞俘,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡盹沈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了吃谣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乞封。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖岗憋,靈堂內的尸體忽然破棺而出歌亲,到底是詐尸還是另有隱情,我是刑警寧澤澜驮,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站惋鸥,受9級特大地震影響杂穷,放射性物質發(fā)生泄漏。R本人自食惡果不足惜卦绣,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一耐量、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧滤港,春花似錦廊蜒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至添履,卻和暖如春屁倔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背暮胧。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工锐借, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留问麸,地道東北人。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓钞翔,卻偏偏與公主長得像严卖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子布轿,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內容