声明:本系列博客整理来源于《Android源码设计模式解析与实战》,仅作为个人学习总结记录,任何组织和个人不得转载进行商业活动!
单一职责原则(Simple Responsibility Priciple,SRP)
SRP的定义是:就一个类而言,应该仅有一个引起它变化的原因。简单来说,一个类中应该是一组相关性很高的函数、数据的封装。就像秦小波老师在《设计模式之禅》中说的:“这是一个备受争议却又极其重要的原则。只要你想和别人争执、怄气或者是吵架,这个原则是屡试不爽的”。因为单一职责的划分界限并不是总是那么清晰,很多时候都是需要靠个人经验来界定。当然,最大的问题就是对职责的定义,什么是类的职责,以及怎么划分职责。
对于计算机技术,通常只单纯的学习理论知识并不能很好的领会其深意,只有自己动手实践,并在实际运用中发现问题、解决问题、思考问题,才能够将知识吸收到自己的脑海中。Android是使用的面向对象设计开发的Java语言,程序中有抽象、接口、六大原则、23种设计模式等。在面向对象设计时,需考虑到其稳定性、可扩展性、灵活性。
毕业后刚开始做Android开发工作的小民同学,在经历过一周的适应期以及熟悉公司的产品、开发规范之后,开发工作正式开始。上级leader挑选了使用范围广、难度也适中的图片加载器ImageLoader,来作为一个小项目来锻炼小民的能力。leader的要求很简单,要小民实现图片加载,并且要将图片缓存起来。小民在分析了需求之后,就开始编码:
/**
* 图片加载类
*/
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR1)
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);
//取1/4的可用内存作为缓存
int cacheSize = maxMemory/4;
mImageCache = new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes()* value.getHeight()/1024;
}
};
}
public void displayImage(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);
}
});
}
private Bitmap downloadImage(String imageUrl) {
Bitmap bitmap = null;
try {
URL url = new URL(imageUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
bitmap = BitmapFactory.decodeStream(conn.getInputStream());
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
}
这时小民对图片加载ImageLoader任务完成,并且使用Git软件进行版本控制,将工程托管到GitHub上。给leader看后,说,“你的ImageLoader耦合太严重了。简直没有设计而言,更不要说扩展性、灵活性了。所有功能都写在一个类中怎么行呢,这样随着功能的增多,ImageLoader类会越来越大,代码也越来越复杂,图片加载系统就越来越脆弱......”小民听后,觉得之前匆忙想着完成任务,把任务想的太简单了。
“你还是把ImageLoader拆分一下,把各个功能独立出来,让它们满足单一职责原则。”leader最后说道。“单一职责原则”,小民敏锐的捕捉到关键词,经过上网查阅资料对它有一些认识,决定对ImageLoader进行一次重构。这次小民先画了一张ImageLoader实现的UML图,如下图所示:
ImageLoader修改如下:
/**
* 图片加载类
*/
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR1)
public class ImageLoader {
private ImageCache mImageCache = new ImageCache();
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() {
Bitmap bitmap = downloadImage(url);
if(bitmap == null) return;
if(imageView.getTag().equals(url)){
imageView.setImageBitmap(bitmap);
}
mImageCache.put(url,bitmap);
}
});
}
private Bitmap downloadImage(String imageUrl) {
Bitmap bitmap = null;
try {
URL url = new URL(imageUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
bitmap = BitmapFactory.decodeStream(conn.getInputStream());
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
}
并且添加了一个ImageCache类用于处理图片缓存,具体代码如下:
/**
* 图片缓存类
*/
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR1)
public class ImageCache {
private LruCache<String,Bitmap> mImageCache;
public ImageCache(){
initImageCache();
}
private void initImageCache() {
//计算可使用的最大内存
int maxMemory = (int)(Runtime.getRuntime().maxMemory());
//取1/4的可用内存作为缓存
int cacheSize = maxMemory/4;
mImageCache = new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes()*value.getHeight()/1024;
}
};
}
public void put(String url,Bitmap bitmap){
mImageCache.put(url,bitmap);
}
public Bitmap get(String url){
return mImageCache.get(url);
}
}
由上述的UML图和代码可看出,小民将ImageLoader一拆为二,ImageLoader只负责图片加载的逻辑,而ImageCache只负责处理图片缓存的逻辑,这样ImageLoader的代码量变少了,职责也清晰了;当与缓存相关的逻辑需要改变时,不需要修改ImageLoader类,而图片加载的逻辑需要修改时也不会影响到缓存处理的逻辑。虽然可扩展性还比较欠缺,但整个代码结构变得清晰许多,基本实现单一职责原则。
正如刚开始我们所说那样,如何划分一个类、一个函数的职责,每个人都有自己的看法,这需要根据个人经验、具体的业务逻辑而定。但是,它也有一些基本的指导原则,例如,两个完全不一样的功能就不应该放在一个类中。一个类中应该是一组相关性很高的函数、数据的封装。工程师可以不断的审视自己的代码,根据具体的业务、功能对类进行相应的拆分,这是程序员优化代码迈出的第一步。