定义
不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责
问题由来
类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。
解决方案
遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。
即便是经验丰富的程序员写出的程序,也会有违背这一原则的代码存在。为什么会出现这种现象呢?因为有职责扩散。所谓职责扩散,就是因为某种原因,由于某种原因,也许是需求变更了,也许是程序的设计者境界提高了,需要将职责P细分为粒度更细的职责P1,P2,这时如果要使程序遵循单一职责原则,需要将类T也分解为两个类T1和T2,分别负责P1、P2两个职责。但是在程序已经写好的情况下,这样做简直太费时间了。所以,简单的修改类T,用它来负责两个职责是一个比较不错的选择,虽然这样做有悖于单一职责原则。(这样做的风险在于职责扩散的不确定性,因为我们不会想到这个职责P,在未来可能会扩散为P1,P2,P3,P4……Pn。所以记住,在职责扩散到我们无法控制的程度之前,立刻对代码进行重构。)
举个栗子
重构前代码
public class ImageLoader {
//图片缓存
private LruCache<String, Bitmap> mImageCache;
//线程池,线程数量为cpu的数量
private ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public ImageLoader() {
initImageCache();
}
private void initImageCache() {
//计算可使用的最大内存,进程能够拿到的最大内存
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
//取四分之一作为缓存
int cacheSize = maxMemory / 4;
mImageCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
//重写sizeOf方法,计算出要缓存的每张图片的大小
protected int sizeOf(String key, Bitmap bitmap) {
//Bitmap所占用的内存空间数等于Bitmap的每一行所占用的空间数乘以Bitmap的行数
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
}
public void displayImage(final String url, final ImageView imageView) {
Bitmap 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);
imageView.post(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(bitmap);
}
})
mImageCache.put(url, bitmap);
}
});
}
public static Bitmap downLoadImage(String imageUrl) {
Bitmap bitmap = null;
try {
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
bitmap = BitmapFactory.decodeStream(connection.getInputStream());
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
}
LruCache
LruCache是个泛型类,主要算法原理是把最近使用的对象用强引用(即我们平常使用的对象引用方式)存储在 LinkedHashMap 中。当缓存满时,把最近最少使用的对象从内存中移除,并提供了get和put方法来完成缓存的获取和添加操作。
ExecutorService
接口 java.util.concurrent.ExecutorService 表述了异步执行的机制,并且可以让任务在后台执行。
重构后代码:
//缓存类
public class ImageCache {
//图片缓存
private LruCache<String, Bitmap> mImageCache;
public ImageCache(){
initImageCache();
}
private void initImageCache() {
//计算可使用的最大内存,进程能够拿到的最大内存
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
//取四分之一作为缓存
final int cacheSize = maxMemory / 4;
//重写sizeOf方法,计算出要缓存的每张图片的大小
mImageCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
//Bitmap所占用的内存空间数等于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);
}
}
//图片加载类
public class ImageLoader {
ImageCache mImageCache = new ImageCache();
//线程池,线程数量为cpu的数量
private ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public void displayImage(final String url, final ImageView imageView) {
Bitmap bitmap = mImageCache.get(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
imageView.setTag(url);
mExecutorService.submit(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = downLoadImage(url);
imageView.post(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(bitmap);
}
})
mImageCache.put(url, bitmap);
}
});
}
public static Bitmap downLoadImage(String imageUrl) {
Bitmap bitmap = null;
try {
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
bitmap = BitmapFactory.decodeStream(connection.getInputStream());
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
}
改变
Imageloader只负责图片的加载,ImageCache只负责图片缓存的逻辑,Imageloader的代码量减少了,职责清晰了,缓存的逻辑需要改变的时候不影响缓存的处理逻辑,图片加载逻辑改变的时候也不会影响到缓存的逻辑。
参考:《Android源码设计模式解析与实战》一书
网友评论