开闭原则

来源:互联网 发布:淘宝导出买家联系方式 编辑:程序博客网 时间:2024/05/21 08:47

转载请注明出处:http://blog.csdn.net/feibendexiaoma/article/details/74089952
1.简称
开闭原则的全称是Open close Principle ,缩写是OCP

2.定义
软件中的对象(类、模块、函数等)应该 对于扩展是开放的,对于修改是封闭的。

3.问题
在软件的生命周期内,因为变化、升有代码进级和维护等原因需要对软件原行修改时,可能会将错误引入原本已经测试过的旧版本中,破环原有系统。

4.解决
尽量使用扩展的方式实现变化,但在实际开发中往往修改原有代码、扩展代码同时进行。
举例:
内存缓存

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;//是否使用双缓存    //线程池 线程数量为CPU的数量    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卡缓存,双缓存,看似挺好的,但是有一个问题,每次在程序中加入新的缓存实现时都需要修改ImageLoader类,然后通过布尔值让用户选择使用哪种缓存,因此在ImageLoader中存在各种if-else判断语句,通过这些判断来确定使用哪些缓存。随着逻辑的引入,代码变得越来越复杂,某一个if条件写错就要花很长时间排除。另外用户不能自己实现缓存注入到ImageLoader中,可扩展性差。

怎么修改呢?
可以通过定义一个接口,里面为公用方法,然后让所有缓存对象实现这个接口,在ImageLoader中定义一个方法setImageCache,用到哪个缓存类就传入哪个缓存类,自由度高。

public class ImageLoader {    ImageCache mImageCache = new MemoryCache();//图片缓存    //注入缓存实现    public void setImageCache(ImageCache cache) {        mImageCache = cache;    }    //线程池 线程数量为CPU的数量    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);    }    /**     * 缓存中没图片 则从网络下载     *     * @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卡缓存,双缓存都实现了该接口。

//内存缓存类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);    }}

那么怎样设置实现缓存呢?

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) 方法注入不同的缓存实现,这样能够使ImageLoader更简单,健壮,也使得ImageLoader的扩展性、灵活性更高。MemoryCache、DiskCache、DoubleCache缓存图片的具体实现完全不一样,但是,它们都实现了ImageCache接口。当用户需要自定义缓存策略时,只需要新建一个实现ImageCache的接口的类,然后构造该类的对象,并且通过setImageCache(ImageCache cache)注入到ImageLoader中,这样ImageLoader就实现了千变万化的缓存策略,且扩展这些缓存策略并不会导致ImageLoader类的修改。

总结

当需求发生变化时,应该尽量通过扩展的方式来实现变化,而不是修改原有代码来实现,尽量遵循开闭原则。
开闭原则的优点:
1.通过扩展已有的软件系统,可以提供新的行为,以满足对软件的新需求,使变化中的软件系统有一定的适应性和灵活性。
2.已有的软件模块,特别是最重要的抽象层模块不能再修改,这就使变化中的软件系统有一定的稳定性和延续性。
3.增加复用性和可维护性。