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.增加复用性和可维护性。