結合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 更好的可擴展性——迪米特原則
通俗的講,一個類應該對本身須要耦合或調用的類知道的最少,類的內部如何實現與調用者不要緊,調用者只須要知道它須要的方法便可。
我的理解:「這個說的就是封裝,儘可能將相同的功能封裝在一個類,只對外暴露須要的接口,其餘的都不可見」。
終於整理完了第一章,這章說的核心思想就是面向對象的三大特色,繼承、封裝、多態。面向接口編程。