美文网首页Android技术研究
我的理解:开闭原则

我的理解:开闭原则

作者: L_Xian | 来源:发表于2016-07-04 15:09 被阅读1072次

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

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

    //内存缓存
    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源码设计模式解析与实践》

    相关文章

      网友评论

        本文标题:我的理解:开闭原则

        本文链接:https://www.haomeiwen.com/subject/szvcjttx.html