[关闭]
@Tyhj 2018-06-13T15:42:37.000000Z 字数 3755 阅读 624

Android设计模式一:单一职责原则

设计模式


定义

不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责

问题由来

类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。所以记住,在职责扩散到我们无法控制的程度之前,立刻对代码进行重构。)

举个栗子

重构前代码

  1. public class ImageLoader {
  2. //图片缓存
  3. private LruCache<String, Bitmap> mImageCache;
  4. //线程池,线程数量为cpu的数量
  5. private ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
  6. public ImageLoader() {
  7. initImageCache();
  8. }
  9. private void initImageCache() {
  10. //计算可使用的最大内存,进程能够拿到的最大内存
  11. int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
  12. //取四分之一作为缓存
  13. int cacheSize = maxMemory / 4;
  14. mImageCache = new LruCache<String, Bitmap>(cacheSize) {
  15. @Override
  16. //重写sizeOf方法,计算出要缓存的每张图片的大小
  17. protected int sizeOf(String key, Bitmap bitmap) {
  18. //Bitmap所占用的内存空间数等于Bitmap的每一行所占用的空间数乘以Bitmap的行数
  19. return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
  20. }
  21. };
  22. }
  23. public void displayImage(final String url, final ImageView imageView) {
  24. Bitmap bitmap = mImageCache.get(url);
  25. if (bitmap != null) {
  26. imageView.setImageBitmap(bitmap);
  27. return;
  28. }
  29. imageView.setTag(url);
  30. mExecutorService.submit(new Runnable() {
  31. @Override
  32. public void run() {
  33. Bitmap bitmap = downLoadImage(url);
  34. imageView.post(new Runnable() {
  35. @Override
  36. public void run() {
  37. imageView.setImageBitmap(bitmap);
  38. }
  39. })
  40. mImageCache.put(url, bitmap);
  41. }
  42. });
  43. }
  44. public static Bitmap downLoadImage(String imageUrl) {
  45. Bitmap bitmap = null;
  46. try {
  47. URL url = new URL(imageUrl);
  48. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  49. bitmap = BitmapFactory.decodeStream(connection.getInputStream());
  50. connection.disconnect();
  51. } catch (Exception e) {
  52. e.printStackTrace();
  53. }
  54. return bitmap;
  55. }
  56. }

LruCache

LruCache是个泛型类,主要算法原理是把最近使用的对象用强引用(即我们平常使用的对象引用方式)存储在 LinkedHashMap 中。当缓存满时,把最近最少使用的对象从内存中移除,并提供了get和put方法来完成缓存的获取和添加操作。

ExecutorService

接口 java.util.concurrent.ExecutorService 表述了异步执行的机制,并且可以让任务在后台执行。

重构后代码:

  1. //缓存类
  2. public class ImageCache {
  3. //图片缓存
  4. private LruCache<String, Bitmap> mImageCache;
  5. public ImageCache(){
  6. initImageCache();
  7. }
  8. private void initImageCache() {
  9. //计算可使用的最大内存,进程能够拿到的最大内存
  10. final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
  11. //取四分之一作为缓存
  12. final int cacheSize = maxMemory / 4;
  13. //重写sizeOf方法,计算出要缓存的每张图片的大小
  14. mImageCache = new LruCache<String, Bitmap>(cacheSize) {
  15. @Override
  16. protected int sizeOf(String key, Bitmap bitmap) {
  17. //Bitmap所占用的内存空间数等于Bitmap的每一行所占用的空间数乘以Bitmap的行数
  18. return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
  19. }
  20. };
  21. }
  22. public void put(String url,Bitmap bitmap){
  23. mImageCache.put(url,bitmap);
  24. }
  25. public Bitmap get(String url){
  26. return mImageCache.get(url);
  27. }
  28. }
  1. //图片加载类
  2. public class ImageLoader {
  3. ImageCache mImageCache = new ImageCache();
  4. //线程池,线程数量为cpu的数量
  5. private ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
  6. public void displayImage(final String url, final ImageView imageView) {
  7. Bitmap bitmap = mImageCache.get(url);
  8. if (bitmap != null) {
  9. imageView.setImageBitmap(bitmap);
  10. return;
  11. }
  12. imageView.setTag(url);
  13. mExecutorService.submit(new Runnable() {
  14. @Override
  15. public void run() {
  16. final Bitmap bitmap = downLoadImage(url);
  17. imageView.post(new Runnable() {
  18. @Override
  19. public void run() {
  20. imageView.setImageBitmap(bitmap);
  21. }
  22. })
  23. mImageCache.put(url, bitmap);
  24. }
  25. });
  26. }
  27. public static Bitmap downLoadImage(String imageUrl) {
  28. Bitmap bitmap = null;
  29. try {
  30. URL url = new URL(imageUrl);
  31. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  32. bitmap = BitmapFactory.decodeStream(connection.getInputStream());
  33. connection.disconnect();
  34. } catch (Exception e) {
  35. e.printStackTrace();
  36. }
  37. return bitmap;
  38. }
  39. }

改变

Imageloader只负责图片的加载,ImageCache只负责图片缓存的逻辑,Imageloader的代码量减少了,职责清晰了,缓存的逻辑需要改变的时候不影响缓存的处理逻辑,图片加载逻辑改变的时候也不会影响到缓存的逻辑。

参考:《Android源码设计模式解析与实战》一书

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注