单一职责原则 英文名称是Single Responsibility Principle,简称SRP
开闭原则 英文全称是Open Close Principle,简称OCP
里氏替换原则 英文全称是Liskov Substitution Principle,简称LSP
依赖倒置原则 英文全称是Dependence Inversion Principle,简称DIP
接口隔离原则 英文全称是InterfaceSegregation Principles,简称ISP
迪米特原则 英文全称为Law of Demeter,简称LOD,也称为最少知识原则(Least Knowledge Principle)
让你的代码更清晰简单——单一职责原则
单一职责原则的英文名称是Single Responsibility Principle,简称SRP。它的定义是:就一个类而言,应该仅有一个引起它变化的原因。简单来说,一个类中应该是一组相关性很高的函数、数据的封装。单一职责的划分界限并不是总是那么清晰,很多时候都是需要靠个人经验来界定。当然,最大的问题就是对职责的定义,什么是类的职责,以及怎么划分类的职责。
下面以项目中实际代码来分析问题,在android app中图片加载是最常见的,于是我们自己动手写一个ImageLoader(图片加载)作为训练项目。如下代码:
public class ImageLoader {
private static ImageLoader Instance = null;
// 图片缓存
LruCache mImageCache;
// 线程池,线程数量为CPU的数量
ExecutorService mExecutorService = Executors.newFixedThreadPool (Runtime.getRuntime().availableProcessors());
//在主线程中显示图片
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
ImageView imageView = (ImageView) msg.obj;
imageView.setImageBitmap((Bitmap) imageView.getTag());
}
};
public static ImageLoader getInstance(){
if(Instance == null){
synchronized (ImageLoader.class) {
if(Instance == null) {
Instance = new ImageLoader();
}
}
}
return Instance;
}
private ImageLoader() {
initImageCache();
}
private void initImageCache() {
// 计算可使用的最大内存
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// 取四分之一的可用内存作为缓存
final int cacheSize = maxMemory / 4;
mImageCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
//Bitmap的每一行所占用的空间数乘以Bitmap的行数
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
}
public void displayImage(final String url, final ImageView imageView) {
imageView.setTag(url);
//先从cache中取图片
if(mImageCache.get(url)!=null){
imageView.setImageBitmap(mImageCache.get(url));
return;
}
mExecutorService.submit(new Runnable() {
@Override
public void run() {
Bitmap bitmap = downloadImage(url);
if (bitmap == null) {
return;
}
if (imageView.getTag().equals(url)) {
Message msg = mHandler.obtainMessage();
imageView.setTag(bitmap);
msg.obj = imageView;
mHandler.sendMessage(msg);
}
mImageCache.put(url, bitmap);
}
});
}
public Bitmap downloadImage(String imageUrl) {
Bitmap bitmap = null;
try {
URL url = new URL(imageUrl);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setDoInput(true); //允许输入流,即允许下载
conn.setUseCaches(false); //不使用缓冲
conn.setRequestMethod("GET"); //使用get请求
InputStream is = conn.getInputStream(); //获取输入流,此时才真正建立链接
bitmap = BitmapFactory.decodeStream(is);
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
}
上面是一个最简单的ImageLoader,在显示图片之前会去判断是否有缓存,但是我们也发现它耦合太严重啦!简直就没有设计可言,更不要说扩展性、灵活性了。所有的功能都写在一个类里,这样随着功能的增多,ImageLoader类会越来越大,代码也越来越复杂,修改起来就是进入hell.
这里我们可以把ImageCahe相关的代码单独拿出来,写成一个单独的类。
public class ImageCache {
// 图片缓存
LruCache mImageCache;
public ImageCache(){
initImageCache();
}
private void initImageCache() {
// 计算可使用的最大内存
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// 取四分之一的可用内存作为缓存
final int cacheSize = maxMemory / 4;
mImageCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
//Bitmap的每一行所占用的空间数乘以Bitmap的行数
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
}
public void put(String key,Bitmap value){
mImageCache.put(key,value);
}
public Bitmap get(String key){
return mImageCache.get(key);
}
}
所以ImageLoader代码需要修改,并且添加了一个ImageCache类用于处理图片缓存,具体代码如下:
public class ImageLoader {
private static ImageLoader Instance = null;
private ImageCache mImageCache;
// 线程池,线程数量为CPU的数量
ExecutorService mExecutorService = Executors.newFixedThreadPool (Runtime.getRuntime().availableProcessors());
//在主线程中显示图片
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
ImageView imageView = (ImageView) msg.obj;
imageView.setImageBitmap((Bitmap) imageView.getTag());
}
};
public static ImageLoader getInstance(){
if(Instance == null){
synchronized (ImageLoader.class) {
if(Instance == null) {
Instance = new ImageLoader();
}
}
}
return Instance;
}
private ImageLoader() {
mImageCache = new ImageCache();
}
public void displayImage(final String url, final ImageView imageView) {
imageView.setTag(url);
//先从cache中取图片
if(mImageCache.get(url)!=null){
imageView.setImageBitmap(mImageCache.get(url));
return;
}
mExecutorService.submit(new Runnable() {
@Override
public void run() {
Bitmap bitmap = downloadImage(url);
if (bitmap == null) {
return;
}
if (imageView.getTag().equals(url)) {
Message msg = mHandler.obtainMessage();
imageView.setTag(bitmap);
msg.obj = imageView;
mHandler.sendMessage(msg);
}
mImageCache.put(url, bitmap);
}
});
}
public Bitmap downloadImage(String imageUrl) {
Bitmap bitmap = null;
try {
URL url = new URL(imageUrl);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setDoInput(true); //允许输入流,即允许下载
conn.setUseCaches(false); //不使用缓冲
conn.setRequestMethod("GET"); //使用get请求
InputStream is = conn.getInputStream(); //获取输入流,此时才真正建立链接
bitmap = BitmapFactory.decodeStream(is);
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
}
和上述代码所示,将ImageLoader一拆为二,ImageLoader只负责图片加载的逻辑,而ImageCache只负责处理图片缓存的逻辑,这样ImageLoader的代码量变少了,职责也清晰了,当与缓存相关的逻辑需要改变时,不需要修改ImageLoader类,而图片加载的逻辑需要修改时也不会影响到缓存处理逻辑。
从上述的例子中我们能够体会到,单一职责所表达出的用意就是“单一”二字。正如上文所说,如何划分一个类、一个函数的职责,每个人都有自己的看法,这需要根据个人经验、具体的业务逻辑而定。但是,它也有一些基本的指导原则,例如,两个完全不一样的功能就不应该放在一个类中。一个类中应该是一组相关性很高的函数、数据的封装。工程师可以不断地审视自己的代码,根据具体的业务、功能对类进行相应的拆分。
代码github地址:点击打开链接