结合Android源码和图片加载的例子,介绍设计模式的同时,在例子中实际运用,更易于读者对设计模式的理解和使用。java
本篇博客结合书中图片加载的例子和本身对知识点的理解,侧重记录下设计模式的使用,原理部分略过。编程
第一章 走向灵活软件之路——面向对象的六大原则设计模式
1.1 优化代码的第一步——单一职责原则缓存
我的理解:“核心思想就是类的抽象和封装,将相同功能的代码封装在一个类中,不一样功能的相互调用经过引用类对象方式实现"ide
下面引用书中的例子,对比下函数
这个是书中的第一个版本,相信你们能发现不少问题,好比每次都建立线程池和缓存,这些后面会进行优化,这块要说的是缓存的处理是一个独立的功能,应该抽象封装到一个类中。一样,下载功能也是如此。优化
public class ImageLoader { private final int BYTE_UNIT = 1024; // 图片缓存 private LruCache<String, Bitmap> mImageCache; // 线程池,线程数量为CPU的数量 private ExecutorService mExecutorService; public ImageLoader() { initImageCache(); mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); } private void initImageCache() { // 计算可以使用的最大内存 final long maxMemory = Runtime.getRuntime().maxMemory() / BYTE_UNIT; final int memorySize = (int) maxMemory / 4; mImageCache = new LruCache<String, Bitmap>(memorySize) { @Override protected int sizeOf(String key, Bitmap value) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // API19 return value.getAllocationByteCount() / BYTE_UNIT; } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) { // API12 return value.getByteCount() / 1024; } // Bitmap所占用的内存空间数等于Bitmap的每一行所占用的空间数乘以Bitmap的行数 return value.getRowBytes() * value.getHeight() / BYTE_UNIT; } }; } /** * 加载图片 * @param url * @param imageView */ 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 url) { Bitmap bitmap = null; HttpURLConnection connection = null; try { connection = (HttpURLConnection) new URL(url).openConnection(); bitmap = BitmapFactory.decodeStream(connection.getInputStream()); } catch (IOException e) { e.printStackTrace(); } finally { if (connection != null) { connection.disconnect(); } } return bitmap; } }
书中在本节只对缓存功能作了封装,咱们来看下封装后的代码。ui
public class ImageCache { private final int BYTE_UNIT = 1024; // 图片缓存 private LruCache<String, Bitmap> mImageCache; public ImageCache() { initImageCache(); } private void initImageCache() { // 计算可以使用的最大内存 final long maxMemory = Runtime.getRuntime().maxMemory() / BYTE_UNIT; final int memorySize = (int) maxMemory / 4; mImageCache = new LruCache<String, Bitmap>(memorySize) { @Override protected int sizeOf(String key, Bitmap value) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // API19 return value.getAllocationByteCount() / BYTE_UNIT; } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) { // API12 return value.getByteCount() / 1024; } // Bitmap所占用的内存空间数等于Bitmap的每一行所占用的空间数乘以Bitmap的行数 return value.getRowBytes() * value.getHeight() / BYTE_UNIT; } }; } public Bitmap get(String url) { return mImageCache.get(url); } public void put(String url, Bitmap bitmap) { mImageCache.put(url, bitmap); } }
public class ImageLoader { // 封装的缓存类 private ImageCache mImageCache = new ImageCache(); // 线程池,线程数量为CPU的数量 private ExecutorService mExecutorService; public ImageLoader() { mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); } /** * 加载图片 * @param url * @param imageView */ 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 url) { ................... } }
1.2 让程序更稳定、更灵活——开闭原则this
我的理解:“就是在设计类时,尽可能考虑其扩展性,保证在有需求变动时,能够不修改原有代码。通常是经过面向接口,经过多态方式实现”。url
软件中的对象(类、模块、函数等)应该对于扩展是开放的,可是对于修改是封闭的,这就是开放——关闭原则。也就是说,当软件须要变化时,咱们应该尽可能经过扩展的方式来实现变化,而不是经过修改已有的代码来实现。
上个例子中,图片缓存咱们使用的ImageCache,主要是内存缓存。
这种问题就是,若是使用者想添加磁盘缓存,则须要更改现有代码,不易于扩展。
改为下面的方式,经过定义接口,调用setImageCache方式实现开发者扩展。
public interface ImageCache { void put(String key, Bitmap bitmap); Bitmap get(String key); } public class MemoryCache implements ImageCache { // 图片缓存 private LruCache<String, Bitmap> mImageCache; .............. @Override public Bitmap get(String url) { return mImageCache.get(url); } @Override public void put(String url, Bitmap bitmap) { mImageCache.put(url, bitmap); } } public class DiskCache implements ImageCache { @Override public void put(String key, Bitmap bitmap) { } @Override public Bitmap get(String key) { return null; } }
public class ImageLoader { // 封装的缓存类 private ImageCache mImageCache = new MemoryCache(); // 线程池,线程数量为CPU的数量 private ExecutorService mExecutorService; public ImageLoader() { mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); } public void setImageCache(ImageCache cache) { this.mImageCache = cache; } ...................... }
1.3 构建扩展性更好的系统——里氏替换原则
我们直接来看第二种定义:全部引用基类的地方必须能透明的使用其子类的对象。
面向对象的语言的三大特色是继承、封装、多态,里氏替换原则就是依赖于继承、多态这两大特性。
1.4 让项目拥有变化的能力——依赖倒置原则
Java语言中的表现就是:模块间的依赖经过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是经过接口或抽象类产生的。其实就是面向接口/抽象编程。
1.5 系统有更高的灵活性——接口隔离原则
定义是:客户端不该该依赖它不须要的接口。接口隔离原则将很是庞大、臃肿的接口拆分红更小的和更具体的接口。接口隔离原则的目的是系统解开耦合,从而容易重构、更改和从新部署。
1.6 更好的可扩展性——迪米特原则
通俗的讲,一个类应该对本身须要耦合或调用的类知道的最少,类的内部如何实现与调用者不要紧,调用者只须要知道它须要的方法便可。
我的理解:“这个说的就是封装,尽可能将相同的功能封装在一个类,只对外暴露须要的接口,其余的都不可见”。
终于整理完了第一章,这章说的核心思想就是面向对象的三大特色,继承、封装、多态。面向接口编程。