學習了何紅輝、關愛民寫的《Android設計模式》,對于面向對象的六大原則有進一步的理解,特此根據(jù)自己的理解記錄總結一下
什么是開閉原則
一個類、模塊、函數(shù)等應該對于擴展是開放的,但是對于修改是封閉的,簡單說就是當你的系統(tǒng)要升級維護需要對原有的代碼進行修改時,可能會將錯誤引入到了原來的舊代碼中,因此,在軟件升級維護時,應該盡可能的通過擴展的方式而不是修改原有的代碼,雖然在實際開發(fā)中,修改原本的代碼跟擴展代碼都是同時存在的,但是為了程序的可擴展性,在制作初期,應該遵循開閉原則。
以上一篇《面向對象的六大原則之 —— 單一原則》的例子為例繼續(xù)講解
在ImageCache類中,使用了內(nèi)存緩存,可是內(nèi)存緩存很有限,而且還容易丟失,比如應用關閉后,原來緩存在內(nèi)存的圖片會丟失,再打開需要重新加載,所以我們考慮加入SD卡緩存。
加入DiskCache類,緩存圖片到SD卡中
[java]view
plaincopy
importandroid.graphics.Bitmap;
importandroid.graphics.BitmapFactory;
importjava.io.FileNotFoundException;
importjava.io.FileOutputStream;
importjava.io.IOException;
/**
*CreatedbyAdministratoron2016/3/1.
*/
publicclassDiskCache{
publicstaticfinalStringcacheDir="sdcard/cache/";
/**
*獲取sd卡的圖片緩存
*@paramurl圖片url
*@return返回bitmap
*/
publicBitmapget(Stringurl){
Bitmapbitmap=null;
bitmap=BitmapFactory.decodeFile(cacheDir+url);
returnbitmap;
}
/**
*緩存到sd卡
*@paramurl圖片的url
*@parambitmapbitmap對象
*/
privatevoidput(Stringurl,Bitmapbitmap){
FileOutputStreamoutput=null;
try{
output=newFileOutputStream(cacheDir+url);
bitmap.compress(Bitmap.CompressFormat.PNG,100,output);
}catch(FileNotFoundExceptione){
e.printStackTrace();
}finally{
//無論是否有異常,都要關閉流
try{
if(output!=null){
output.close();
}
}catch(IOExceptione){
e.printStackTrace();
}
}
}
}
因為加入了sd卡的緩存方式,所以要修改ImageLoader類
[java]view
plaincopy
importandroid.graphics.Bitmap;
importandroid.graphics.BitmapFactory;
importandroid.widget.ImageView;
importjava.io.IOException;
importjava.net.HttpURLConnection;
importjava.net.MalformedURLException;
importjava.net.URL;
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
/**
*CreatedbyAdministratoron2016/3/1.
*/
publicclassImageLoader{
publicImageLoader(){
}
//內(nèi)存緩存類
ImageCacheimageCache=newImageCache();
//sd卡緩存類
DiskCachediskCache=newDiskCache();
//是否使用sd卡緩存
booleanisUseDiskCache=false;
//線程池,線城數(shù)量為cpu的數(shù)量
ExecutorServicemExecutorService=Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
/**
*顯示圖片
*@paramurl圖片的url
*@paramimageView要顯示的view
*/
publicvoiddisplayImage(finalStringurl,finalImageViewimageView){
Bitmapbitmap=isUseDiskCache()?diskCache.get(url):imageCache.get(url);
if(bitmap!=null){
imageView.setImageBitmap(bitmap);
return;
}
imageView.setTag(url);
mExecutorService.submit(newRunnable(){
@Override
publicvoidrun(){
Bitmapbitmap=downloadImage(url);
if(bitmap==null){
return;
}
if(imageView.getTag().equals(url)){
imageView.setImageBitmap(bitmap);
}
imageCache.put(url,bitmap);
diskCache.put(url,bitmap);
}
});
}
/**
*下載圖片
*@paramimageUrl網(wǎng)絡圖片地址
*@return返回bitmap對象
*/
publicBitmapdownloadImage(StringimageUrl){
Bitmapbitmap=null;
try{
URLurl=newURL(imageUrl);
HttpURLConnectionconn=(HttpURLConnection)url.openConnection();
bitmap=BitmapFactory.decodeStream(conn.getInputStream());
conn.disconnect();
}catch(MalformedURLExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}
returnbitmap;
}
publicbooleanisUseDiskCache(){
returnisUseDiskCache;
}
publicvoidsetIsUseDiskCache(booleanisUseDiskCache){
this.isUseDiskCache=isUseDiskCache;
}
}
ImageLoader類中加入了少量代碼就添加了sd卡緩存的功能,用戶還能通過setIsUseDiskCache設置使用哪種緩存,但是以上還有個問題,使用內(nèi)存緩存的時候,就沒有sd卡緩存,用戶要兩種策略都用,首先緩存優(yōu)先使用內(nèi)存緩存, 如果內(nèi)存沒有就查找sd卡緩存,sd卡沒有就從網(wǎng)絡獲取,,這才是最好的緩存策略,依照這種想法,修改代碼,加入DoubleCache類,實現(xiàn)雙緩存
[java]view
plaincopy
importandroid.graphics.Bitmap;
/**
*CreatedbyAdministratoron2016/3/1.
*/
publicclassDoubleCache{
//內(nèi)存緩存
ImageCachemImageCache=newImageCache();
//sd卡緩存
DiskCachemDiskCache=newDiskCache();
/**
*獲取緩存圖片
*@paramurl圖片url
*@return返回bitmap對象
*/
publicBitmapget(Stringurl){
Bitmapbitmap=null;
bitmap=mImageCache.get(url);
if(bitmap==null){
bitmap=mDiskCache.get(url);
}
returnbitmap;
}
/**
*緩存圖片
*@paramurl圖片url
*@parambitmap
*/
publicvoidput(Stringurl,Bitmapbitmap){
mImageCache.put(url,bitmap);
mDiskCache.put(url,bitmap);
}
}
ImageLoader類修改如下
[java]view
plaincopy
importandroid.graphics.Bitmap;
importandroid.graphics.BitmapFactory;
importandroid.widget.ImageView;
importjava.io.IOException;
importjava.net.HttpURLConnection;
importjava.net.MalformedURLException;
importjava.net.URL;
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
/**
*CreatedbyAdministratoron2016/3/1.
*/
publicclassImageLoader{
publicImageLoader(){
}
//內(nèi)存緩存類
ImageCacheimageCache=newImageCache();
//sd卡緩存類
DiskCachediskCache=newDiskCache();
//雙緩存
DoubleCachedoubleCache=newDoubleCache();
//是否使用sd卡緩存
booleanisUseDiskCache=false;
//是否使用雙緩存
booleanisUseDoubleCache=false;
//線程池,線城數(shù)量為cpu的數(shù)量
ExecutorServicemExecutorService=Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
/**
*顯示圖片
*@paramurl圖片的url
*@paramimageView要顯示的view
*/
publicvoiddisplayImage(finalStringurl,finalImageViewimageView){
Bitmapbitmap=null;
if(isUseDoubleCache()){
bitmap=doubleCache.get(url);
}elseif(isUseDiskCache()){
bitmap=diskCache.get(url);
}else{
bitmap=imageCache.get(url);
}
if(bitmap!=null){
imageView.setImageBitmap(bitmap);
return;
}
imageView.setTag(url);
mExecutorService.submit(newRunnable(){
@Override
publicvoidrun(){
Bitmapbitmap=downloadImage(url);
if(bitmap==null){
return;
}
if(imageView.getTag().equals(url)){
imageView.setImageBitmap(bitmap);
}
//緩存到內(nèi)存跟sd卡中
doubleCache.put(url,bitmap);
}
});
}
/**
*下載圖片
*@paramimageUrl網(wǎng)絡圖片地址
*@return返回bitmap對象
*/
publicBitmapdownloadImage(StringimageUrl){
Bitmapbitmap=null;
try{
URLurl=newURL(imageUrl);
HttpURLConnectionconn=(HttpURLConnection)url.openConnection();
bitmap=BitmapFactory.decodeStream(conn.getInputStream());
conn.disconnect();
}catch(MalformedURLExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}
returnbitmap;
}
publicbooleanisUseDiskCache(){
returnisUseDiskCache;
}
publicvoidsetIsUseDiskCache(booleanisUseDiskCache){
this.isUseDiskCache=isUseDiskCache;
}
publicbooleanisUseDoubleCache(){
returnisUseDoubleCache;
}
publicvoidsetIsUseDoubleCache(booleanisUseDoubleCache){
this.isUseDoubleCache=isUseDoubleCache;
}
}
經(jīng)過修改之后,我們的圖片加載器已經(jīng)實現(xiàn)了雙緩存的功能了,可是細心的你可能發(fā)現(xiàn)了,每次加入新的緩存方法都要修改原來的代碼,而且還會讓ImageLoader變的越來越復雜,一堆的if{}else{}判斷,條件多了就容易造成錯誤,而且如果現(xiàn)在用戶想要自己擴展一個緩存方式,都要動ImageLoader類里面的東西,本文最上面已經(jīng)說了,開閉原則的目的就是為了讓擴展是開發(fā)的,修改是封閉的,當軟件需要變化時,我們應該盡可能的通過擴展的方式來實現(xiàn)變化,而不要通過修改已有的代碼來實現(xiàn),按照這個思維,我們來想想應該如何修改,首先,我們的ImageLoader的職責就是加載圖片跟設置緩存的,而緩存的實現(xiàn)不由它管,我們的目的是讓緩存的方式能讓用戶自己擴展,所以我們定義ImageCache接口,接口中有兩個方法,一個是put(String
url,Bitmap bitmap)用來添加到緩存,一個是get(String url);用來獲取緩存,具體實現(xiàn)不管,而是由實現(xiàn)了這個接口的實現(xiàn)類來具體實現(xiàn)。
ImageCache類如下:
[java]view
plaincopy
importandroid.graphics.Bitmap;
/**
*CreatedbyAdministratoron2016/3/1.
*/
publicinterfaceImageCache{
/**
*添加緩存
*@paramurl緩存的圖片url作為緩存的key
*@parambitmap緩存的bitmap
*/
publicvoidput(Stringurl,Bitmapbitmap);
/**
*獲取緩存的圖片
*@paramurl
*@return
*/
publicBitmapget(Stringurl);
}
我們可以將接口當成是一個標準,比如電腦的usb接口,想要通過usb連接到電腦,就必須要按照usb這個插口的標準走,不然你想插也插不進去,我們的圖片加載器也是一樣,想要自己擴展一個緩存方式,就要按我的標準來。
所以,上面的內(nèi)存緩存、sd卡緩存、雙緩存三個類,我們?nèi)啃薷膶崿F(xiàn)ImageCache類
內(nèi)存緩存類(MemoryCache)
[java]view plaincopy importandroid.graphics.Bitmap; importandroid.util.LruCache; /** *CreatedbyAdministratoron2016/3/1. */ publicclassMemoryCacheimplementsImageCache{ publicMemoryCache(){ //初始化圖片緩存 initImageCache(); } //圖片緩存類 LruCache