[关闭]
@Tyhj 2018-07-13T00:55:54.000000Z 字数 5723 阅读 846

Android设计模式二:开闭原则

设计模式


原文链接:https://www.zybuluo.com/Tyhj/note/1192305

定义

软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是对于修改是封闭的

核心思想

尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化

一个软件产品在生命周期内,都会发生变化,既然变化是一个既定的事实,我们就应该在设计的时候尽量适应这些变化,以提高项目的稳定性和灵活性。

举个栗子

在之前的图片加载框架,新增本地缓存功能,tag 1.2

  1. public class DiskCache {
  2. //保存位置
  3. String cacheDir = "sdcard/cache/";
  4. //获取图片
  5. public Bitmap get(String url) {
  6. return BitmapFactory.decodeFile(cacheDir + url);
  7. }
  8. //保存图片到本地
  9. public void put(String url, Bitmap bitmap) {
  10. FileOutputStream fileOutputStream = null;
  11. fileOutputStream = new FileOutputStream(cacheDir + url);
  12. bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
  13. }
  14. }

新增了图片缓存到SD卡中,所以ImageLoad代码有更新

  1. public class ImageLoader {
  2. //内存缓存
  3. ImageCache mImageCache = new ImageCache();
  4. //SD卡缓存
  5. DiskCache mDiskCache = new DiskCache();
  6. //是否使用本地缓存
  7. boolean isUseDiskCache = false;
  8. public void setUseDiskCache(boolean useDiskCache) {
  9. isUseDiskCache = useDiskCache;
  10. }
  11. //线程池,线程数量为cpu的数量
  12. private ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
  13. public void displayImage(final String url, final ImageView imageView, final Activity context) {
  14. final Bitmap bitmap = isUseDiskCache ? mDiskCache.get(url) : mImageCache.get(url);
  15. if (bitmap != null) {
  16. imageView.setImageBitmap(bitmap);
  17. return;
  18. }
  19. imageView.setTag(url);
  20. mExecutorService.submit(new Runnable() {
  21. @Override
  22. public void run() {
  23. final Bitmap bitmap = downLoadImage(url);
  24. if (isUseDiskCache) {
  25. mDiskCache.put(url, bitmap);
  26. } else {
  27. mImageCache.put(url, bitmap);
  28. }
  29. imageView.setImageBitmap(bitmap);
  30. }
  31. });
  32. }
  33. public static Bitmap downLoadImage(String imageUrl) {
  34. Bitmap bitmap = null;
  35. URL url = new URL(imageUrl);
  36. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  37. bitmap = BitmapFactory.decodeStream(connection.getInputStream());
  38. connection.disconnect();
  39. return bitmap;
  40. }
  41. }

代码完成,但是功能不是很合理,因为两种缓存不能同时使用,实际上,先从内存获取没有找到再从本地读取,本地也没有再从网络获取,才是最好的策略。

新增一个双重缓存的类

  1. public class DoubleCache {
  2. ImageCache imageCache = new ImageCache();
  3. DiskCache diskCache = new DiskCache();
  4. /**
  5. * 双缓存,获取图片的时候先从内存中读取,如果没有再从SD卡中读取
  6. *
  7. * @param url
  8. * @return
  9. */
  10. public Bitmap get(String url) {
  11. Bitmap bitmap = imageCache.get(url);
  12. if (bitmap == null) {
  13. bitmap = diskCache.get(url);
  14. imageCache.put(url, bitmap);
  15. }
  16. return bitmap;
  17. }
  18. //将图片缓存到内存和SD卡中
  19. public void put(String url, Bitmap bitmap) {
  20. imageCache.put(url, bitmap);
  21. diskCache.put(url, bitmap);
  22. }
  23. }

再次修改ImageLoad代码

  1. public class ImageLoader {
  2. //内存缓存
  3. ImageCache mImageCache = new ImageCache();
  4. //SD卡缓存
  5. DiskCache mDiskCache = new DiskCache();
  6. //双重缓存
  7. DoubleCache mDoubleCache = new DoubleCache();
  8. //是否使用本地缓存
  9. boolean isUseDiskCache = false;
  10. //双缓存
  11. boolean isUseDoubleCache = false;
  12. public void setUseDiskCache(boolean useDiskCache) {
  13. isUseDiskCache = useDiskCache;
  14. }
  15. public void setUseDoubleCache(boolean useDoubleCache) {
  16. isUseDoubleCache = useDoubleCache;
  17. }
  18. //线程池,线程数量为cpu的数量
  19. private ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
  20. public void displayImage(final String url, final ImageView imageView, final Activity context) {
  21. Bitmap bitmap = null;
  22. if (isUseDoubleCache) {
  23. bitmap = mDoubleCache.get(url);
  24. } else if (isUseDiskCache) {
  25. bitmap = mDiskCache.get(url);
  26. } else {
  27. bitmap = mImageCache.get(url);
  28. }
  29. if (bitmap != null) {
  30. final Bitmap finalBitmap = bitmap;
  31. imageView.setImageBitmap(finalBitmap);
  32. return;
  33. }
  34. imageView.setTag(url);
  35. mExecutorService.submit(new Runnable() {
  36. @Override
  37. public void run() {
  38. final Bitmap bitmap = downLoadImage(url);
  39. if (isUseDoubleCache) {
  40. mDoubleCache.put(url, bitmap);
  41. } else if (isUseDiskCache) {
  42. mDiskCache.put(url, bitmap);
  43. } else {
  44. mImageCache.put(url, bitmap);
  45. }
  46. imageView.setImageBitmap(bitmap);
  47. }
  48. });
  49. }
  50. public static Bitmap downLoadImage(String imageUrl) {
  51. Bitmap bitmap = null;
  52. try {
  53. URL url = new URL(imageUrl);
  54. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  55. bitmap = BitmapFactory.decodeStream(connection.getInputStream());
  56. connection.disconnect();
  57. } catch (Exception e) {
  58. e.printStackTrace();
  59. }
  60. return bitmap;
  61. }
  62. }

双缓存完成了,但是存在一些问题,每次添加新功能的时候都要去修改ImageLoader源码,很可能引入bug,而且使得原来的代码越来越复杂、脆弱,不小心写错一个if条件就需要花费时间来排查,而且用户使用我们的框架,不能自己实现缓存注入到ImageLoader中,可扩展性差

image_1cgr988tvpcp1lj2j3thlg1moo22.png-45.7kB

开闭原则重构代码,ImageLoader

  1. public class ImageLoader {
  2. //内存缓存
  3. ImageCache mImageCache = new MemoryCache();
  4. public void setmImageCache(ImageCache mImageCache) {
  5. this.mImageCache = mImageCache;
  6. }
  7. //线程池,线程数量为cpu的数量
  8. private ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
  9. public void displayImage(final String url, final ImageView imageView, final Activity context) {
  10. Bitmap bitmap = mImageCache.get(url);
  11. if (bitmap != null) {
  12. final Bitmap finalBitmap = bitmap;
  13. imageView.setImageBitmap(finalBitmap);
  14. return;
  15. }
  16. imageView.setTag(url);
  17. mExecutorService.submit(new Runnable() {
  18. @Override
  19. public void run() {
  20. final Bitmap bitmap = downLoadImage(url);
  21. mImageCache.put(url, bitmap);
  22. imageView.setImageBitmap(bitmap);
  23. }
  24. });
  25. }
  26. public static Bitmap downLoadImage(String imageUrl) {
  27. Bitmap bitmap = null;
  28. try {
  29. URL url = new URL(imageUrl);
  30. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  31. bitmap = BitmapFactory.decodeStream(connection.getInputStream());
  32. connection.disconnect();
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. return bitmap;
  37. }
  38. }

缓存继承ImageCache接口

  1. public interface ImageCache {
  2. public Bitmap get(String url);
  3. public void put(String url, Bitmap bmp);
  4. }

使用方法:

  1. //双重缓存
  2. imageLoader = new ImageLoader();
  3. imageLoader.setmImageCache(new DoubleCache());
  4. imageLoader.displayImage(url, iv_test1, this);
  5. //自定义缓存栗子
  6. imageLoader.setmImageCache(new ImageCache() {
  7. @Override
  8. public Bitmap get(String url) {
  9. return null;
  10. }
  11. @Override
  12. public void put(String url, Bitmap bmp) {
  13. }
  14. });

重构完成

通过setImageCache(IamgeCache iamgeCache)方法注入不同的缓存实现,这样不仅能够使ImageLoader更简单、健壮,也使得Imageloader的灵活性、可扩展性更高。只需要新建一个实现ImageCache接口的类,注入到ImageLoader中,ImageLoader就可以实现各种各样的缓存策略,而且这些缓存策略不会导致ImageLoader类的修改。

开闭原则的理解

用抽象构建框架,用实现扩展细节。因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节,我们用从抽象派生的实现类来进行扩展,当软件需要发生变化时,我们只需要根据需求重新派生一个实现类来扩展就可以了。当然前提是我们的抽象要合理,要对需求的变更有前瞻性和预见性才行。

该图片加载框架集成方法:

  1. //Step 1. Add the JitPack repository to your build file
  2. allprojects {
  3. repositories {
  4. ...
  5. maven { url 'https://jitpack.io' }
  6. }
  7. }
  1. //Step 2. Add the dependency
  2. dependencies {
  3. implementation 'com.github.tyhjh:DesignMode:v1.3'
  4. }

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

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