zoukankan      html  css  js  c++  java
  • 面向对象的六大原则之 —— 开闭原则

    学习了何红辉、关爱民写的《Android设计模式》,对于面向对象的六大原则有进一步的理解,特此根据自己的理解记录总结一下

    什么是开闭原则

    一个类、模块、函数等应该对于扩展是开放的,但是对于修改是封闭的,简单说就是当你的系统要升级维护需要对原有的代码进行修改时,可能会将错误引入到了原来的旧代码中,因此,在软件升级维护时,应该尽可能的通过扩展的方式而不是修改原有的代码,虽然在实际开发中,修改原本的代码跟扩展代码都是同时存在的,但是为了程序的可扩展性,在制作初期,应该遵循开闭原则。


    以上一篇《面向对象的六大原则之 —— 单一原则》的例子为例继续讲解

    在ImageCache类中,使用了内存缓存,可是内存缓存很有限,而且还容易丢失,比如应用关闭后,原来缓存在内存的图片会丢失,再打开需要重新加载,所以我们考虑加入SD卡缓存。


    加入DiskCache类,缓存图片到SD卡中

    [java] view plain copy
    1. import android.graphics.Bitmap;  
    2. import android.graphics.BitmapFactory;  
    3.   
    4. import java.io.FileNotFoundException;  
    5. import java.io.FileOutputStream;  
    6. import java.io.IOException;  
    7.   
    8. /** 
    9.  * Created by Administrator on 2016/3/1. 
    10.  */  
    11. public class DiskCache {  
    12.     public static final String cacheDir="sdcard/cache/";  
    13.   
    14.     /** 
    15.      * 获取sd卡的图片缓存 
    16.      * @param url 图片url 
    17.      * @return 返回bitmap 
    18.      */  
    19.     public Bitmap get(String url){  
    20.         Bitmap bitmap=null;  
    21.         bitmap= BitmapFactory.decodeFile(cacheDir+url);  
    22.         return bitmap;  
    23.     }  
    24.   
    25.     /** 
    26.      * 缓存到sd卡 
    27.      * @param url 图片的url 
    28.      * @param bitmap bitmap对象 
    29.      */  
    30.     private void put(String url ,Bitmap bitmap){  
    31.         FileOutputStream output = null;  
    32.         try {  
    33.             output = new FileOutputStream(cacheDir+url);  
    34.             bitmap.compress(Bitmap.CompressFormat.PNG,100,output);  
    35.         } catch (FileNotFoundException e) {  
    36.             e.printStackTrace();  
    37.         }finally {  
    38.             //无论是否有异常,都要关闭流  
    39.             try {  
    40.                 if(output!=null){  
    41.                     output.close();  
    42.                 }  
    43.             } catch (IOException e) {  
    44.                 e.printStackTrace();  
    45.             }  
    46.         }  
    47.   
    48.     }  
    49. }  

    因为加入了sd卡的缓存方式,所以要修改ImageLoader类

    [java] view plain copy
    1. import android.graphics.Bitmap;  
    2. import android.graphics.BitmapFactory;  
    3. import android.widget.ImageView;  
    4.   
    5. import java.io.IOException;  
    6. import java.net.HttpURLConnection;  
    7. import java.net.MalformedURLException;  
    8. import java.net.URL;  
    9. import java.util.concurrent.ExecutorService;  
    10. import java.util.concurrent.Executors;  
    11.   
    12. /** 
    13.  * Created by Administrator on 2016/3/1. 
    14.  */  
    15. public class ImageLoader {  
    16.   
    17.     public ImageLoader() {  
    18.     }  
    19.     //内存缓存类  
    20.     ImageCache imageCache=new ImageCache();  
    21.     //sd卡缓存类  
    22.     DiskCache diskCache=new DiskCache();  
    23.     //是否使用sd卡缓存  
    24.     boolean isUseDiskCache=false;  
    25.     //线程池,线城数量为cpu的数量  
    26.     ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());  
    27.   
    28.     /** 
    29.      * 显示图片 
    30.      * @param url 图片的url 
    31.      * @param imageView 要显示的view 
    32.      */  
    33.     public void displayImage(final String url, final ImageView imageView) {  
    34.         Bitmap bitmap=isUseDiskCache()?diskCache.get(url):imageCache.get(url);  
    35.         if(bitmap!=null){  
    36.             imageView.setImageBitmap(bitmap);  
    37.             return;  
    38.         }  
    39.         imageView.setTag(url);  
    40.         mExecutorService.submit(new Runnable() {  
    41.             @Override  
    42.             public void run() {  
    43.                 Bitmap bitmap = downloadImage(url);  
    44.                 if (bitmap == null) {  
    45.                     return;  
    46.                 }  
    47.                 if (imageView.getTag().equals(url)) {  
    48.                     imageView.setImageBitmap(bitmap);  
    49.                 }  
    50.                 imageCache.put(url, bitmap);  
    51.         diskCache.put(url,bitmap);  
    52.             }  
    53.         });  
    54.     }  
    55.     /** 
    56.      * 下載圖片 
    57.      * @param imageUrl 网络图片地址 
    58.      * @return 返回bitmap对象 
    59.      */  
    60.     public Bitmap downloadImage(String imageUrl) {  
    61.         Bitmap bitmap = null;  
    62.         try {  
    63.             URL url = new URL(imageUrl);  
    64.             HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
    65.             bitmap = BitmapFactory.decodeStream(conn.getInputStream());  
    66.             conn.disconnect();  
    67.         } catch (MalformedURLException e) {  
    68.             e.printStackTrace();  
    69.         } catch (IOException e) {  
    70.             e.printStackTrace();  
    71.         }  
    72.         return bitmap;  
    73.     }  
    74.   
    75.     public boolean isUseDiskCache() {  
    76.         return isUseDiskCache;  
    77.     }  
    78.   
    79.     public void setIsUseDiskCache(boolean isUseDiskCache) {  
    80.         this.isUseDiskCache = isUseDiskCache;  
    81.     }  
    82. }  


    ImageLoader类中加入了少量代码就添加了sd卡缓存的功能,用户还能通过setIsUseDiskCache设置使用哪种缓存,但是以上还有个问题,使用内存缓存的时候,就没有sd卡缓存,用户要两种策略都用,首先缓存优先使用内存缓存, 如果内存没有就查找sd卡缓存,sd卡没有就从网络获取,,这才是最好的缓存策略,依照这种想法,修改代码,加入DoubleCache类,实现双缓存

    [java] view plain copy
    1. import android.graphics.Bitmap;  
    2.   
    3. /** 
    4.  * Created by Administrator on 2016/3/1. 
    5.  */  
    6. public class DoubleCache {  
    7.     //内存缓存  
    8.     ImageCache mImageCache=new ImageCache();  
    9.     //sd卡缓存  
    10.     DiskCache mDiskCache=new DiskCache();  
    11.   
    12.     /** 
    13.      * 获取缓存图片 
    14.      * @param url 图片url 
    15.      * @return 返回bitmap对象 
    16.      */  
    17.     public Bitmap get(String url){  
    18.         Bitmap bitmap=null;  
    19.         bitmap=mImageCache.get(url);  
    20.         if(bitmap==null){  
    21.             bitmap=mDiskCache.get(url);  
    22.         }  
    23.         return bitmap;  
    24.     }  
    25.   
    26.     /** 
    27.      * 缓存图片 
    28.      * @param url 图片url 
    29.      * @param bitmap  
    30.      */  
    31.     public void put(String url,Bitmap bitmap){  
    32.         mImageCache.put(url,bitmap);  
    33.         mDiskCache.put(url,bitmap);  
    34.     }  
    35. }  

    ImageLoader类修改如下

    [java] view plain copy
    1. import android.graphics.Bitmap;  
    2. import android.graphics.BitmapFactory;  
    3. import android.widget.ImageView;  
    4.   
    5. import java.io.IOException;  
    6. import java.net.HttpURLConnection;  
    7. import java.net.MalformedURLException;  
    8. import java.net.URL;  
    9. import java.util.concurrent.ExecutorService;  
    10. import java.util.concurrent.Executors;  
    11.   
    12. /** 
    13.  * Created by Administrator on 2016/3/1. 
    14.  */  
    15. public class ImageLoader {  
    16.   
    17.     public ImageLoader() {  
    18.     }  
    19.   
    20.     //内存缓存类  
    21.     ImageCache imageCache=new ImageCache();  
    22.     //sd卡缓存类  
    23.     DiskCache diskCache=new DiskCache();  
    24.     //双缓存  
    25.     DoubleCache doubleCache=new DoubleCache();  
    26.     //是否使用sd卡缓存  
    27.     boolean isUseDiskCache=false;  
    28.     //是否使用双缓存  
    29.     boolean isUseDoubleCache=false;  
    30.     //线程池,线城数量为cpu的数量  
    31.     ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());  
    32.   
    33.   
    34.   
    35.     /** 
    36.      * 显示图片 
    37.      * @param url 图片的url 
    38.      * @param imageView 要显示的view 
    39.      */  
    40.     public void displayImage(final String url, final ImageView imageView) {  
    41.         Bitmap bitmap=null;  
    42.         if(isUseDoubleCache()){  
    43.             bitmap=doubleCache.get(url);  
    44.         }else if(isUseDiskCache()){  
    45.             bitmap=diskCache.get(url);  
    46.         }else{  
    47.             bitmap=imageCache.get(url);  
    48.         }  
    49.         if(bitmap!=null){  
    50.             imageView.setImageBitmap(bitmap);  
    51.             return;  
    52.         }  
    53.         imageView.setTag(url);  
    54.         mExecutorService.submit(new Runnable() {  
    55.             @Override  
    56.             public void run() {  
    57.                 Bitmap bitmap = downloadImage(url);  
    58.                 if (bitmap == null) {  
    59.                     return;  
    60.                 }  
    61.                 if (imageView.getTag().equals(url)) {  
    62.                     imageView.setImageBitmap(bitmap);  
    63.                 }  
    64.                 //缓存到内存跟sd卡中  
    65.                 doubleCache.put(url, bitmap);  
    66.             }  
    67.         });  
    68.     }  
    69.     /** 
    70.      * 下載圖片 
    71.      * @param imageUrl 网络图片地址 
    72.      * @return 返回bitmap对象 
    73.      */  
    74.     public Bitmap downloadImage(String imageUrl) {  
    75.         Bitmap bitmap = null;  
    76.         try {  
    77.             URL url = new URL(imageUrl);  
    78.             HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
    79.             bitmap = BitmapFactory.decodeStream(conn.getInputStream());  
    80.             conn.disconnect();  
    81.         } catch (MalformedURLException e) {  
    82.             e.printStackTrace();  
    83.         } catch (IOException e) {  
    84.             e.printStackTrace();  
    85.         }  
    86.         return bitmap;  
    87.     }  
    88.   
    89.     public boolean isUseDiskCache() {  
    90.         return isUseDiskCache;  
    91.     }  
    92.   
    93.     public void setIsUseDiskCache(boolean isUseDiskCache) {  
    94.         this.isUseDiskCache = isUseDiskCache;  
    95.     }  
    96.   
    97.     public boolean isUseDoubleCache() {  
    98.         return isUseDoubleCache;  
    99.     }  
    100.   
    101.     public void setIsUseDoubleCache(boolean isUseDoubleCache) {  
    102.         this.isUseDoubleCache = isUseDoubleCache;  
    103.     }  
    104. }  

    经过修改之后,我们的图片加载器已经实现了双缓存的功能了,可是细心的你可能发现了,每次加入新的缓存方法都要修改原来的代码,而且还会让ImageLoader变的越来越复杂,一堆的if{}else{}判断,条件多了就容易造成错误,而且如果现在用户想要自己扩展一个缓存方式,都要动ImageLoader类里面的东西,本文最上面已经说了,开闭原则的目的就是为了让扩展是开发的,修改是封闭的,当软件需要变化时,我们应该尽可能的通过扩展的方式来实现变化,而不要通过修改已有的代码来实现,按照这个思维,我们来想想应该如何修改,首先,我们的ImageLoader的职责就是加载图片跟设置缓存的,而缓存的实现不由它管,我们的目的是让缓存的方式能让用户自己扩展,所以我们定义ImageCache接口,接口中有两个方法,一个是put(String url,Bitmap bitmap)用来添加到缓存,一个是get(String url);用来获取缓存,具体实现不管,而是由实现了这个接口的实现类来具体实现。

    ImageCache类如下:

    [java] view plain copy
    1. import android.graphics.Bitmap;  
    2.   
    3. /** 
    4.  * Created by Administrator on 2016/3/1. 
    5.  */  
    6. public interface ImageCache {  
    7.     /** 
    8.      * 添加缓存 
    9.      * @param url    缓存的图片url作为缓存的key 
    10.      * @param bitmap 缓存的bitmap 
    11.      */  
    12.     public void put(String url, Bitmap bitmap);  
    13.   
    14.     /** 
    15.      * 获取缓存的图片 
    16.      * @param url 
    17.      * @return 
    18.      */  
    19.     public Bitmap get(String url);  
    20. }  

    我们可以将接口当成是一个标准,比如电脑的usb接口,想要通过usb连接到电脑,就必须要按照usb这个插口的标准走,不然你想插也插不进去,我们的图片加载器也是一样,想要自己扩展一个缓存方式,就要按我的标准来。

    所以,上面的内存缓存、sd卡缓存、双缓存三个类,我们全部修改实现ImageCache类

    内存缓存类(MemoryCache)

    [java] view plain copy
    1. import android.graphics.Bitmap;  
    2. import android.util.LruCache;  
    3.   
    4. /** 
    5.  * Created by Administrator on 2016/3/1. 
    6.  */  
    7. public class MemoryCache implements ImageCache{  
    8.   
    9.     public MemoryCache() {  
    10.         //初始化图片缓存  
    11.         initImageCache();  
    12.     }  
    13.   
    14.     //图片缓存类  
    15.     LruCache<String, Bitmap> memoryCache;  
    16.   
    17.     /** 
    18.      * 初始化缓存 
    19.      */  
    20.     private void initImageCache() {  
    21.         //计算可使用的最大内存  
    22.         int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
    23.         //只用最大内存的四分之一作为缓存大小  
    24.         int cacheSize = maxMemory / 4;  
    25.         memoryCache = new LruCache<String, Bitmap>(cacheSize) {  
    26.             @Override  
    27.             protected int sizeOf(String key, Bitmap value) {  
    28.                 return value.getRowBytes() * value.getHeight() / 1024;  
    29.             }  
    30.         };  
    31.     }  
    32.   
    33.     /** 
    34.      * 添加缓存 
    35.      * 
    36.      * @param url    缓存的图片url作为缓存的key 
    37.      * @param bitmap 缓存的bitmap 
    38.      */  
    39.     public void put(String url, Bitmap bitmap) {  
    40.         memoryCache.put(url, bitmap);  
    41.     }  
    42.   
    43.     /** 
    44.      * 获取缓存的图片 
    45.      * 
    46.      * @param url 
    47.      * @return 
    48.      */  
    49.     public Bitmap get(String url) {  
    50.         Bitmap bitmap = null;  
    51.         bitmap = memoryCache.get(url);  
    52.         return bitmap;  
    53.     }  
    54. }  

    SD卡缓存类(DiskCache)

    [java] view plain copy
    1. import android.graphics.Bitmap;  
    2. import android.graphics.BitmapFactory;  
    3.   
    4. import java.io.FileNotFoundException;  
    5. import java.io.FileOutputStream;  
    6. import java.io.IOException;  
    7.   
    8. /** 
    9.  * Created by Administrator on 2016/3/1. 
    10.  */  
    11. public class DiskCache implements ImageCache{  
    12.     public static final String cacheDir="sdcard/cache/";  
    13.   
    14.     /** 
    15.      * 获取sd卡的图片缓存 
    16.      * @param url 图片url 
    17.      * @return 返回bitmap 
    18.      */  
    19.     public Bitmap get(String url){  
    20.         Bitmap bitmap=null;  
    21.         bitmap= BitmapFactory.decodeFile(cacheDir+url);  
    22.         return bitmap;  
    23.     }  
    24.   
    25.     /** 
    26.      * 缓存到sd卡 
    27.      * @param url 图片的url 
    28.      * @param bitmap bitmap对象 
    29.      */  
    30.     public void put(String url ,Bitmap bitmap){  
    31.         FileOutputStream output = null;  
    32.         try {  
    33.             output = new FileOutputStream(cacheDir+url);  
    34.             bitmap.compress(Bitmap.CompressFormat.PNG,100,output);  
    35.         } catch (FileNotFoundException e) {  
    36.             e.printStackTrace();  
    37.         }finally {  
    38.             //无论是否有异常,都要关闭流  
    39.             try {  
    40.                 if(output!=null){  
    41.                     output.close();  
    42.                 }  
    43.             } catch (IOException e) {  
    44.                 e.printStackTrace();  
    45.             }  
    46.         }  
    47.   
    48.     }  
    49. }  

    双缓存类(DoubleCache)

    [java] view plain copy
    1. import android.graphics.Bitmap;  
    2.   
    3. /** 
    4.  * Created by Administrator on 2016/3/1. 
    5.  */  
    6. public class DoubleCache implements ImageCache{  
    7.     //内存缓存  
    8.     MemoryCache mMemoryCache =new MemoryCache();  
    9.     //sd卡缓存  
    10.     DiskCache mDiskCache=new DiskCache();  
    11.   
    12.     /** 
    13.      * 获取缓存图片 
    14.      * @param url 图片url 
    15.      * @return 返回bitmap对象 
    16.      */  
    17.     public Bitmap get(String url){  
    18.         Bitmap bitmap=null;  
    19.         bitmap= mMemoryCache.get(url);  
    20.         if(bitmap==null){  
    21.             bitmap=mDiskCache.get(url);  
    22.         }  
    23.         return bitmap;  
    24.     }  
    25.   
    26.     /** 
    27.      * 缓存图片 
    28.      * @param url 图片url 
    29.      * @param bitmap 
    30.      */  
    31.     public void put(String url,Bitmap bitmap){  
    32.         mMemoryCache.put(url,bitmap);  
    33.         mDiskCache.put(url,bitmap);  
    34.     }  
    35. }  

    最后我们的ImageLoader类修改如下:

    [java] view plain copy
    1. import android.graphics.Bitmap;  
    2. import android.graphics.BitmapFactory;  
    3. import android.widget.ImageView;  
    4.   
    5. import java.io.IOException;  
    6. import java.net.HttpURLConnection;  
    7. import java.net.MalformedURLException;  
    8. import java.net.URL;  
    9. import java.util.concurrent.ExecutorService;  
    10. import java.util.concurrent.Executors;  
    11.   
    12. /** 
    13.  * Created by Administrator on 2016/3/1. 
    14.  */  
    15. public class ImageLoader {  
    16.   
    17.     public ImageLoader() {  
    18.     }  
    19.   
    20.     //默认使用内存缓存  
    21.     ImageCache imageCache=new MemoryCache();  
    22.   
    23.     //线程池,线城数量为cpu的数量  
    24.     ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());  
    25.   
    26.     /** 
    27.      * 设置要使用哪种缓存 
    28.      * @param imageCache 缓存类 
    29.      */  
    30.     public void setImageCache(ImageCache imageCache) {  
    31.         this.imageCache = imageCache;  
    32.     }  
    33.   
    34.     /** 
    35.      * 显示图片 
    36.      * @param url 图片的url 
    37.      * @param imageView 要显示的view 
    38.      */  
    39.     public void displayImage(final String url, final ImageView imageView) {  
    40.         Bitmap bitmap=imageCache.get(url);  
    41.   
    42.         if(bitmap!=null){  
    43.             imageView.setImageBitmap(bitmap);  
    44.             return;  
    45.         }  
    46.         imageView.setTag(url);  
    47.         mExecutorService.submit(new Runnable() {  
    48.             @Override  
    49.             public void run() {  
    50.                 Bitmap bitmap = downloadImage(url);  
    51.                 if (bitmap == null) {  
    52.                     return;  
    53.                 }  
    54.                 if (imageView.getTag().equals(url)) {  
    55.                     imageView.setImageBitmap(bitmap);  
    56.                 }  
    57.                 //添加到缓存  
    58.                 imageCache.put(url, bitmap);  
    59.             }  
    60.         });  
    61.     }  
    62.   
    63.     /** 
    64.      * 下載圖片 
    65.      * @param imageUrl 网络图片地址 
    66.      * @return 返回bitmap对象 
    67.      */  
    68.     public Bitmap downloadImage(String imageUrl) {  
    69.         Bitmap bitmap = null;  
    70.         try {  
    71.             URL url = new URL(imageUrl);  
    72.             HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
    73.             bitmap = BitmapFactory.decodeStream(conn.getInputStream());  
    74.             conn.disconnect();  
    75.         } catch (MalformedURLException e) {  
    76.             e.printStackTrace();  
    77.         } catch (IOException e) {  
    78.             e.printStackTrace();  
    79.         }  
    80.         return bitmap;  
    81.     }  
    82. }  

    在ImageLoader中,我们去掉了其他的缓存类,只加入了ImageCache接口,而且还开放了一个设置缓存的方法,也就是通常说的依赖注入,经过以上修改之后,用户的使用方式如下:

    [java] view plain copy
    1. <span style="white-space:pre">    </span>ImageLoader imageLoader=new ImageLoader();  
    2.         /** 
    3.          * 内存缓存方式 
    4.          */  
    5.         imageLoader.setImageCache(new MemoryCache());  
    6.         /** 
    7.          * SD卡缓存方式 
    8.          */  
    9.         imageLoader.setImageCache(new DiskCache());  
    10.         /** 
    11.          * 双缓存方式 
    12.          */  
    13.         imageLoader.setImageCache(new DoubleCache());  
    14.         /** 
    15.          * 自定义缓存方式 
    16.          */  
    17.         imageLoader.setImageCache(new ImageCache() {  
    18.             @Override  
    19.             public void put(String url, Bitmap bitmap) {  
    20.                 //用户自定义的缓存方式  
    21.             }  
    22.   
    23.             @Override  
    24.             public Bitmap get(String url) {  
    25.                 //从缓存中获取图片  
    26.                 return null;  
    27.             }  
    28.         });  
    通过以上的修改,发现现在的ImageLoader的可扩展性、灵活性变的很高,三种缓存方式各自的具体实现都不一样,但是他们都实现了ImageCache接口,即有了这个标准,而用户想要自定义缓存实现的时候,只需要实现ImageCache接口,将其setImageCache进去即可。

    以上就是开闭原则,对于修改是封闭的,对于扩展是开发的,遵循开闭原则的重要手段应该是通过抽象,由于实际开发中,可能会不尽人意,但是我们在设计的时候,应该尽量通过扩展的方式,这里说尽量是因为,可能原本的代码就是有问题的,必须要修改。

  • 相关阅读:
    Single Round Match 811 1~4 题解
    AtCoder Beginner Contest 215 A~F 题解
    Codeforces Round #739 (Div. 3) 题解
    Codeforces Round #737 (Div. 2) A~E 题解
    Codeforces Round #735 (Div. 2) A~D 题解
    CodeChef Starters 6 Division 3 题解
    AtCoder Beginner Contest 209 题解
    Codeforces Round #732 (Div. 2) A~D题解
    【YBTOJ】约数之和
    【Luogu P7794】[COCI2014-2015#7] JANJE
  • 原文地址:https://www.cnblogs.com/zhaoshujie/p/9594667.html
Copyright © 2011-2022 走看看