我的理解:开闭原则

定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
也就是说,如果修改或者添加一个功能,应该是通过扩展原来的代码,而不是通过修改原来的代码。

比如,在图片加载类中,有内存缓存,磁盘缓存,还有双缓存:

//内存缓存
public class MemoryCache {

    LruCache<String, Bitmap> mLruCache;

    public MemoryCache() {
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 4;
        mLruCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight() / 1024;
            }
        };
    }

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

    public void put(String url, Bitmap bmp) {
        // do something
    }
}

//磁盘缓存
public class DiskCache {
    public Bitmap get(String url) {
        return BitmapFactory.decodeFile(url);
    }

    public void put(String url, Bitmap bmp) {
       //do something
    }
}

//双缓存
public class DoubleCache {
    MemoryCache mMemoryCache = new MemoryCache();
    DiskCache mDiskCache = new DiskCache();

    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 bmp) {
        //do something
    }
}

如果不用开闭原则,那么在图片加载类中,可能会这么写:

public class ImageLoader {

    MemoryCache mMemoryCache = new MemoryCache();
    DiskCache mDiskCache = new DiskCache();
    DoubleCache mDoubleCache = new DoubleCache();

    boolean isDiskCache = false; //使用磁盘缓存
    boolean isDoubleCache = false; //使用双缓存

    public ImageLoader() {
    }

    public void displayImage(String url, ImageView mImageView) {
        Bitmap bitmap = null;
        if (isDoubleCache) {
            bitmap = mDoubleCache.get(url);
        } else if (isDiskCache) {
            bitmap = mDiskCache.get(url);
        } else {
            bitmap = mMemoryCache.get(url);
        }
        if (bitmap != null) {
            mImageView.setImageBitmap(bitmap);
        } else {
            //没有缓存,下载图片
        }
    }

    public void setDiskCache(boolean diskCache) {
        isDiskCache = diskCache;
    }

    public void setDoubleCache(boolean doubleCache) {
        isDoubleCache = doubleCache;
    }
}

这样写,可能会出现这样的问题:
if-else 的判断条件太多,如果写错了其中一个,就会化很多时间去查找和解决,ImageLoader 类也会变得臃肿,而且用户不能自己实现缓存注入到 ImageLoader 中,可扩展性差。

那么,通过分析可以知道,每个缓存都用 set 和 put 方法,那么可以把它抽象出来。
UML图:

《我的理解:开闭原则》 UML

这样通过接口就可以实现不同的缓存,而且不需要改变 ImageLoader 的代码。

首先创建接口 ImageCache:

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

然后将原来的三种缓存都继承它:

//内存缓存
public class MemoryCache implements ImageCache {

    LruCache<String, Bitmap> mLruCache;

    public MemoryCache() {
        //初始化LruCache
    }

    @Override
    public void put(String url, Bitmap bmp) {
        mLruCache.put(url, bmp);
    }

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

//磁盘缓存
public class DiskCache implements ImageCache{

    @Override
    public void put(String url, Bitmap bmp) {
        //将Bitmap写入文件
    }

    @Override
    public Bitmap get(String url) {
       return BitmapFactory.decodeFile(url); //从文件中获取Bitmap
    }
}

//双缓存
public class DoubleCache implements ImageCache {
    ImageCache mMemoryCache = new MemoryCache();
    ImageCache mDiskCache = new DiskCache();

    @Override
    public void put(String url, Bitmap bmp) {
        mMemoryCache.put(url, bmp);
        mDiskCache.put(url, bmp);
    }

    @Override
    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 MemoryCache();

    public void setImageCache(ImageCache imageCache) {
        mImageCache = imageCache;
    }

    public ImageLoader() {
    }

    public void displayImage(String url, ImageView mImageView) {
        Bitmap bitmap = mImageCache.get(url);
        if (bitmap != null) {
            mImageView.setImageBitmap(bitmap);
        }
        //图片没缓存,下载
        // do something
    }
}

比原来的简洁很多。

那么在使用的时候这样:

ImageLoader imageLoader = new ImageLoader();
//选择使用内存缓存
imageLoader.setImageCache(new MemoryCache());
//选择使用磁盘缓存
imageLoader.setImageCache(new DiskCache());
//选择使用双缓存
imageLoader.setImageCache(new DoubleCache());
//不选择封装好的缓存,自己实现缓存
imageLoader.setImageCache(new ImageCache() {
    @Override
    public void put(String url, Bitmap bmp) {

    }

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

可以看到,用户通过 setImageCache 方法可以自由设置缓存的实现方式,而不用通过修改 ImageLoader 来实现。setImageCache 就是通常说的依赖注入。使得 ImageLoader 类更加健壮,这就是开闭原则。

这里,应该明白开闭原则是怎么一回事了。

参考:《Android源码设计模式解析与实践》

    原文作者:L_Xian
    原文地址: https://www.jianshu.com/p/2eeaf9b46fcc
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞