开闭原则 - (让程序更稳定,更灵活)
上一篇:
面向对象基本原则 - 单一职责
开闭原则是java里最基础的设计原则
定义是:对象(类,模块,函数等)应该对于扩展是开放的,对于修改是封闭的
当需要对代码进行修改,这个时候应该尽量去扩展原来的代码,而不是去修改原来的代码,修改原来的代码就有可能会引起其他的问题
这里还是拿上一篇 单一职责 的例子来说明
由于之前的ImageLoader
只有内存缓存
有一天,我们想要增加功能,给ImageLoader
增加SD卡本地缓存,并且提供一个方法,让调用者来选择使用内存缓存还是本地缓存,于是代码就要修改了
添加本地缓存DiskCache类,这里只是学习基本原则,就不使用系统的DiskLruCache类了
public class DiskCache {
private static final String cacheDir = "sdcard/cache";
public Bitmap get(String imageUrl){
return BitmapFactory.decodeFile(cacheDir + imageUrl);
}
public void put(String imageUrl,Bitmap bitmap){
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(cacheDir + imageUrl);
bitmap.compress(Bitmap.CompressFormat.PNG,100,fileOutputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
if(fileOutputStream != null){
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
于是ImageLoader就修改成这样:
public class ImageLoader {
private ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private ImageCache mImageCache = new ImageCache();
private DiskCache mDiskCache = new DiskCache();
private boolean isDiskCache;
//对外提供方法设置,是否是本地缓存
public void setDiskCache(boolean diskCache) {
isDiskCache = diskCache;
}
public void displayImage(final String url, final ImageView imageView) {
//判断是本地缓存还是内存缓存
Bitmap bitmap = isDiskCache ? mDiskCache.get(url) : mImageCache.get(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
imageView.setTag(url);
//没有缓存,提交到线程池下载
submitLoad(url, imageView);
}
...
}
代码看起来也还可以,但是如果有一天,我们希望内存和本地两种缓存都使用,如果内存缓存有就从内存里面取,内存没有再从本地里面取,本地也没有,再从网络上下载图片
修改代码,添加一个双缓存类DoubleCache
public class DoubleCache {
private ImageCache mMemoryCache = new ImageCache();
private DiskCache mDiskCache = new DiskCache();
public Bitmap get(String url){
Bitmap bitmap = mMemoryCache.get(url);
if(bitmap == null){
bitmap = mDiskCache.get(url);
mMemoryCache.put(url,bitmap);
}
return bitmap;
}
public void put(String url,Bitmap bitmap){
mMemoryCache.put(url,bitmap);
mDiskCache.put(url,bitmap);
}
}
然后ImageLoader
也要修改判断了
public class ImageLoader {
private ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private ImageCache mMemoryCache = new ImageCache();
private DiskCache mDiskCache = new DiskCache();
private DoubleCache mDoubleCache = new DoubleCache();
private boolean isDiskCache;
private boolean isDoubleCache;
//对外提供方法设置,是否是本地缓存
public void setDiskCache(boolean diskCache) {
isDiskCache = diskCache;
}
//是否使用双重缓存
public void setDoubleCache(boolean doubleCache) {
isDoubleCache = doubleCache;
}
public void displayImage(final String url, final ImageView imageView) {
//判断是本地缓存还是内存缓存
Bitmap bitmap = null;
if(isDoubleCache){
bitmap = mDoubleCache.get(url);
}else if(isDiskCache){
bitmap = mDiskCache.get(url);
}else {
bitmap = mMemoryCache.get(url);
}
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
imageView.setTag(url);
//没有缓存,提交到线程池下载
submitLoad(url, imageView);
}
}
到这里就会发现,每次修改缓存功能的时候,都要修改ImageLoader
类,然后通过一系列的判断来选择使用那种缓存,使得if-else越来越多,代码越来越复杂,如果不小心写错某个if判断,容易出现错误,也会让ImageLoader
越来越臃肿,更重要的是,用户不能实现自己的缓存注入到ImageLoader
中,可扩展性差
而开闭原则指明,对象(类,模块,函数等)应该对于扩展是开放的,对于修改是封闭的,所以这里应该重构ImageLoader
的代码,使得结构更加清晰,稳定
抽象一个顶层接口,各种缓存来实现这个接口,统一管理
public interface ImageCache {
Bitmap get(String url);
void put(String url,Bitmap bitmap);
}
public class ImageLoader {
private ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
//默认实现
private ImageCache mImageCache = new MemoryCache();
public void setImageCache(ImageCache cache){
mImageCache = cache;
}
public void displayImage(final String url, final ImageView imageView) {
Bitmap bitmap = mImageCache.get(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
imageView.setTag(url);
//没有缓存,提交到线程池下载
submitLoad(url, imageView);
}
}
使用:
ImageLoader imageLoader = new ImageLoader();
//只使用内存缓存
imageLoader.setImageCache(new MemoryCache());
//只使用本地缓存
imageLoader.setImageCache(new DiskCache());
//使用双缓存
imageLoader.setImageCache(new DoubleCache());
//自定义缓存图片实现
imageLoader.setImageCache(new ImageCache() {
@Override
public Bitmap get(String url) {
return null;
}
@Override
public void put(String url, Bitmap bitmap) {
//自定义缓存图片
}
});
重构后的代码没有了那么多 if-else判断,少了各种各样的缓存对象实例,代码清晰简洁.ImageLoader
变得更加稳定,扩展性和灵活性更高,当需要自定义缓存的时候,实现ImageCache
接口就可以,并且通过setImageCache()方法注入到ImageLoader
遵循开闭原则的最重要的一点是抽象,用抽象去构建框架,用实现扩展细节;这样当发生修改的时候,直接实现抽象,派生一个类去实现不同的修改
参考资料:
《Android源码设计模式解析与实战》
网友评论