面向对象的六大原则之开闭原则

       在软件生命周期内,因为变化、升级、维护等原因需要对原代码进行修改时,可能会将错误引入到已经经过测试的旧代码中,从而破坏原有系统。因此当软件需要变化的时候,我们应该尽量通过扩展的方式实现变化,而不是通过修改已经存在的代码来实现。
      开闭原则简单来说就是,软件中的对象(类、方法等)应该对于扩展是开放的,对于修改时关闭的。然而在显示软件开发过程中,不修改代码是不现实的,我们能做的就是尽量遵循开闭原则、此前提下我们也有必要了解什么是开闭原则,并在生产环境中灵活的趋使用它。
      我们还以上一章的例子进行讲解,这里给一下上一文章的链接《面向对象的六大原则之单一职责原则》上文将代码重构之后结构较为清晰了,然而我们知道Android的应用内存有限,内存缓存的图片具有易失性,应用重新启动需要重新下载,会消耗用户比较多的流量,这个时候应该怎么办呢?这个时候我们可以加入SD卡缓存。我们先来看一下一般性的代码实现方式:
     先增加一个实现SD卡缓存的类

public class DiskCache {
    //声明存储路径
    static  String cacheDir="sdcard/cache/";

    /**
     * 取缓存图片
     * @param url
     * @return
     */
    public Bitmap get(String url){
        return BitmapFactory.decodeFile(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 (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(fileOutputStream!=null)
            {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

上一篇文章的实现内存缓存的ImageCache类

public class ImageCache {
    LruCache mImageCache;
    public ImageCache(){
        initImageCache();
    }

    private void initImageCache()
    {
        //计算可以使用的最大内存
        final int maxMemory= (int) (Runtime.getRuntime().maxMemory()/1024);
        //取可用内存的1/4
        final int cachesize=maxMemory/4;
        mImageCache=new LruCache(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);
    }
}

最后是一个负责加载图片的ImageLoader类:

public class ImageLoader {

    DiskCache diskCache = new DiskCache();
    ImageCache imageCache = new ImageCache();
    boolean isUseDiskCache = false;
    ExecutorService mExecutorService = 
 Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    public void displayImage(final String url, final ImageView imageView) {
        Bitmap bitmap = isUseDiskCache ? diskCache.get(url) : imageCache.get(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }else{
        //   没有图片则从网络上下载图片,并放入缓存
        //   downBitmap(url);
        }


    }

    public void setUseDiskCache(boolean useDiskCache) {
        isUseDiskCache = useDiskCache;
    }


    /**
     * 通过URL下载图片,并返回Bitmap
     *
     * @param imageUrl
     * @return
     */
    public Bitmap downBitmap(String imageUrl) {
        Bitmap bitmap = null;
        try {
            URL url = new URL(imageUrl);
            final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            bitmap = BitmapFactory.decodeStream(connection.getInputStream());
            connection.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }
}

最后我们怎么使用这个类呢?请看以下代码:

ImageLoader imageloader=new ImageLoader();
//使用SD卡缓存
imageloader.setUseDisCache(true);
//使用内存进行缓存
imageloader.setUseDisCache(false);

      以上代码确实解决了内存缓存图片易丢失的问题,然而功能实现的是比较僵硬的,用户使用内存缓存,就不能用SD卡缓存。这对使用这一功能的程序员来说是比较难以接受的。我们熟知的正确的缓存功能是应该优先使用内存缓存,如果内存缓存中没有再使用SD卡缓存,如果SD卡中也没有图片最后再从网络中获取图片,并放入缓存中。我们通过代码看看怎么来实现:

/**
*首先要对SD卡缓存和内存缓存进行整合,实现一个双缓存,使其先从内存获取图片,
再从SD卡中获取图片。我们来写一下整合后的代码:
*/
public class DoubleCache{
        ImageCache mMemoryCache=new ImageCache();
        DiskCache mDiskCache=new DiskCache();
        public Bitmap get(String url)
          {
              //从缓存中获取图片
              Bitmap bitmap=mMemoryCache.get(url);
              if(bitmap==null){
                    bitmap=mDiskCache.get(url)
                }
            }
         public void put(String url,Bitmap bmp){
                mMemoryCache.put(bmp);
                mDiskCache.put(bmp)
          }
}

/**
*以下再次修改一下ImageLoader
*/
public class ImageLoader {

    DiskCache diskCache = new DiskCache();//SD卡缓存
    ImageCache imageCache = new ImageCache();//内存缓存
    DoubleCache doubleCache=new DoubleCache();//新增双缓存功能类
    boolean isDoubleCache=false;//是否使用双缓存代码
    boolean isUseDiskCache = false;是否使用SD卡缓存
    ExecutorService mExecutorService = 
    Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    public void displayImage(final String url, final ImageView imageView) {
        Bitmap bitmap = null;
        if(isDoubleCache){
            bitmap=doubleCache.get(url);
        }else if(isUseDiskCache)
        {
             bitmap=diskCache.get(url);
        }else{
             bitmap=imageCache.get(url);
          }
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
        }


    }

    public void setUseDiskCache(boolean useDiskCache) {
        isUseDiskCache = useDiskCache;
    }
   public void setDoubleCache(boolean useDoubleCache) {
        isDoubleCache = useDoubleCache;
    }

以上代码在内存缓存、SD缓存的基础上再次添加了双缓存,用户可以根据自己的需求选择使用SD卡缓存,内存缓存或者双缓存。这样的代码虽说有功能,但是看着实在太丑陋了,项目中的需求各种各样,假如十个人都需要使用与其他人不同的缓存策略,岂不是要在代码中添加十几个if...else..语句吗?不断地修改代码,难保不会出现BUG。这是非常不符合开闭原则的。我们对这个功能依照开闭原则来做一下整理:

/**
*首先声明一个接口,提取各种缓存的公共方法
*/
public interface ImageCache{
      //获取Bitmap
      public Bitmap get(String url);
      //将图片放入缓存中
      public void put(String url,Bitmap bmp);
}
/**
*接着实现内存缓存、SD卡缓存、双缓存等其他自定义的缓存策略全部通过ImageCache接口来实现。代码如下
*/
public class MemoryCache implenments ImageCache{
       private LruCache mMemoryCache;
       public MemoryCache(){
       }
       @Override
       public Bitmap get(String url){
            return mMemoryCache.get(url);
       }
       @Override
       public void put(String url,Bitmap bmp){
            mMemoryCache.put(url,bmp);
       }
}

public class DiskCache implenments ImageCache{
    //声明存储路径
    static  String cacheDir="sdcard/cache/";

    /**
     * 取缓存图片
     * @param url
     * @return
     */
    @Override
    public Bitmap get(String url){
        return BitmapFactory.decodeFile(url);
    }
    @Override
    public void put(String url,Bitmap bitmap){
        FileOutputStream fileOutputStream=null;
        try {
            fileOutputStream=new FileOutputStream(cacheDir+url);
            bitmap.compress(Bitmap.CompressFormat.PNG,100,fileOutputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(fileOutputStream!=null)
            {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

public class DoubleCache implenments ImageCache{
        ImageCache mMemoryCache=new MemoryCache();
        ImageCache mDiskCache=new DiskCache();
        public Bitmap get(String url)
          {
              //从缓存中获取图片
              Bitmap bitmap=mMemoryCache.get(url);
              if(bitmap==null){
                    bitmap=mDiskCache.get(url)
                }
            }
         public void put(String url,Bitmap bmp){
                mMemoryCache.put(bmp);
                mDiskCache.put(bmp)
          }
}

我们再修改一下ImageLoader,在这个类中我们就可以使用ImageCache来代替实现各个缓存策略。代码如下:

public class ImageLoader {
    //内存缓存为其默认缓存
    ImageCache imageCache = new MemoryCache();
    ExecutorService mExecutorService =Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
     //通过这个方法制定缓存策略
     public void setImageCache(ImageCache cache){
            imageCache=cache;
      }
    public void displayImage(final String url, final ImageView imageView) {
        Bitmap bitmap =  imageCache.get(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }else{
        //   没有图片则从网络上下载图片,并放入缓存。此处代码自行实现
        //   downBitmap(url);
        }


    }

在上述代码中,通过setImageCache()方法指定不同的缓存策略即可,这样不仅能够使Imageloader更简单、健壮,也使得ImageLoader的可扩展性灵活性更高以上三种缓存实现完全不一样,共同点是都实现了ImageCache接口。用户需要自定义实现缓存策略时,只需要新建一个实现ImageCache接口的类,通过setImageCache方法注入ImageLoader中即可,这样以来ImageLoader就实现了千变万化的缓存策略,并且对于扩展这些缓存策略并不会导致ImageLoader类的修改。这就是我们说的对扩展开放,对修改封闭的软件开闭原则。遵循开闭原则的重要手段就是通过抽象。

你可能感兴趣的:(面向对象的六大原则之开闭原则)