1.前言
最近重溫了各種設計模式,順便寫了下筆記,分享出來給有緣的人,希望此係列文章能幫助到你們,那
將是我寫此係列文章的最有用的價值之一了。
學中作,做中學,這是學技術最好的方法之一,所以我幾乎都是通過例子來說明問題,這樣文章就有點
長,可是如果肯研究下,定會有收穫,如果只講理論原理,那似乎懂了,可是做的時候還是不會用,所以
踏踏實實的去動手是你進步的源泉了。本系列文章用到的例子是從書籍《Android 源碼設計模式解析與實戰》中獲取,在此感謝非常作者的汗水。若侵權則刪。由於表達的需要對這些例子做不同程度的修改。《Android 源碼設計模式解析與實戰》這本書很適合學習,大家可以直接去買來研究。
那爲啥要學設計模式呢?等你敲代碼到一定程度了自然就明白了。否則你學着會覺得沒有,這是我的真
實感受。在正式的學習經典的各種設計模式之前,我們先來看看設計模式的六大原則,這些原則都將會
體現在設計模式之中。
2.單一職責概念
單一職責,簡單來說就是一個類中一組相關性很高的函數、數據的封裝。用外行話來說,就是通常一個
類只負責一個功能。但是這個單一職責的界限並沒有那麼清晰的,需要自己的個人經驗來確定,不要陷
入去區分什麼纔算是單一職責的漩渦裏。等下我們會說明例子來說明這個問題。單一職責是優化代碼的
第一步。
3.菜鳥的寫法
- 需求:圖片加載並顯示圖片,並將圖片緩存起來。
package com.study.demo.designdemo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.util.LruCache;
import android.widget.ImageView;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 圖片加載類
* Created by WKC on 2017/7/8.
*/
public class ImageLoader {
//圖片緩存
LruCache<String, Bitmap> mImageCache;
//線程池,線程數量爲CUP的數量
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().
availableProcessors());
//UI Handler
Handler mUiHandler = new Handler(Looper.getMainLooper());
//構造函數
public ImageLoader() {
initImageCache();
}
//初始化緩存內存的參數
private void initImageCache() {
//計算可使用的最大內存
final long maxMemory = Runtime.getRuntime().maxMemory() / 1024;
//取四分之一的可用內存作爲緩存
final int cacheSize = (int) (maxMemory / 4);
mImageCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
}
/**
* 提供外部調用顯示圖片的函數
*
* @param url 圖片的路徑
* @param imageview 顯示圖片的view
*/
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)) {
updateImageview(imageview, bitmap);
}
mImageCache.put(url, bitmap);
}
});
}
/**
* 在UI線程顯示圖片
*
* @param imageview
* @param bitmap
*/
private void updateImageview(final ImageView imageview, final Bitmap bitmap) {
mUiHandler.post(new Runnable() {
@Override
public void run() {
imageview.setImageBitmap(bitmap);
}
});
}
/**
* 下載圖片
*
* @param imegeUrl
* @return
*/
private Bitmap downloadImage(String imegeUrl) {
Bitmap bitmap = null;
try {
URL url = new URL(imegeUrl);
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
bitmap = BitmapFactory.decodeStream(conn.getInputStream());
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
}
ok,寫好了,能順利的加載圖片,能顯示,也能緩存,似乎萬事大吉了。可是如果功能不斷的增多,這
個類就會變得越來越臃腫,代碼也越來越複雜,圖片加載系統就會越來越脆弱,那麼我們該怎麼做呢?
我們來分析下:這個類都做了哪些事?
- 1.加載圖片;
- 2.緩存圖片;
- 3.顯示圖片。
主要做了這三件事。剛纔我們說了單一職責,就是一個類中應該是一組相關性很高的函數、數據的封
裝。而這個類顯然不符合這個要求。我們姑且把緩存的職責提出來,生成一個鮮明的單一職責的類–
緩存,這樣做的具體好處,會在下篇文章《六大設計原則之開閉原則》中有體現。現在我們把緩存提
取出來。當然我們也可以把下載的職責提出來,這個就留給你來練手了。
我們先看下UML圖:
如圖所示:如果對UML圖還不瞭解,這篇文章可以幫助到你
http://www.uml.org.cn/oobject/201211231.asp
4.ImageLoader 代碼修改
package com.study.demo.designdemo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.util.LruCache;
import android.widget.ImageView;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 圖片加載類
* Created by WKC on 2017/7/8.
*/
public class ImageLoader {
//圖片緩存
private ImageCache mImageCache = new ImageCache();
//線程池,線程數量爲CUP的數量
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().
availableProcessors());
//UI Handler
Handler mUiHandler = new Handler(Looper.getMainLooper());
/**
* 提供外部調用顯示圖片的函數
*
* @param url 圖片的路徑
* @param imageview 顯示圖片的view
*/
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)) {
updateImageview(imageview, bitmap);
}
mImageCache.put(url, bitmap);
}
});
}
/**
* 在UI線程顯示圖片
*
* @param imageview
* @param bitmap
*/
private void updateImageview(final ImageView imageview, final Bitmap bitmap) {
mUiHandler.post(new Runnable() {
@Override
public void run() {
imageview.setImageBitmap(bitmap);
}
});
}
/**
* 下載圖片
*
* @param imegeUrl
* @return
*/
private Bitmap downloadImage(String imegeUrl) {
Bitmap bitmap = null;
try {
URL url = new URL(imegeUrl);
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
bitmap = BitmapFactory.decodeStream(conn.getInputStream());
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
}
5.提取出來的緩存類
package com.study.demo.designdemo;
import android.graphics.Bitmap;
import android.util.LruCache;
/**
* Created by WKC on 2017/7/8.
*/
public class ImageCache {
//圖片緩存
LruCache<String, Bitmap> mImageCache;
public ImageCache() {
initImageCache();
}
//初始化緩存內存的參數
private void initImageCache() {
//計算可使用的最大內存
final long maxMemory = Runtime.getRuntime().maxMemory() / 1024;
//取四分之一的可用內存作爲緩存
final int cacheSize = (int) (maxMemory / 4);
mImageCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
}
public void put(String url, Bitmap bitmap) {
mImageCache.put(url, bitmap);
}
public Bitmap get(String url) {
return mImageCache.get(url);
}
}
這樣一拆分爲二,ImageLoader負責圖片加載並顯示的邏輯(讀者可以將加載和顯示繼續拆分,當做
練手),而ImageCache類只負責處理緩存的邏輯。這個代碼清晰了,如果需要修改某個邏輯時,如
對ImageCache類的邏輯進行修改,儘管在這個類中改好了,不必去打擾ImageLoader類。
從以上的例子中,再來說下單一職責的意思,所謂單一,就是其職能唯一或者類似的一個功能,但是
沒有嚴格的界限,每個人都有自己的理解,這需要經驗和具體的業務邏輯來定的。儘管如此,還是有
一些指導的原則,兩個功能完全不一樣的就不應該放在同一個類中。一個類中應該是相關性很高的函
數或者數據的封裝。