在日常開發過程中時常需要用到設計模式,但是設計模式有23種,如何將這些設計模式了然于胸并且能在實際開發過程中應用得得心應手呢?和我一起跟著《Android源碼設計模式解析與實戰》一書邊學邊應用吧!
今天我們要講的是面向對象的六大原則
單一職責原則
就一個類而言,應該僅有一個引起它變化的原因。簡單來說,一個類中應該是一組相關性很高的函數、數據的封裝。
- 我們在App中往往會用到很多的公共方法,比如獲取系統的時間,這個功能可能在App中的很多地方都要用到。這個時候我們一般會單獨寫個工具類TimeUtils,把處理時間有關的方法都放到這個類里面,這樣就能減少重復代碼,App的結構會更加清晰。當需要添加其他跟時間有關的方法時,就可以都加到這個TimeUtils類里面,這就是我們平時遵循的單一職責原則。
開閉原則
軟件中的對象(類、模塊、函數等),應該對于擴展是開放的,而對于修改是封閉的。
- 我們在軟件開發過程中就要考慮到后續的擴展和修改。比如說,我們在開發一款類似于universal-image-loader的圖片加載框架,可能一開始我們的功能比較簡單,圖片緩存只有內存緩存。當我們新版本需要添加SD卡緩存時,就要注意盡可能的減少對原來代碼的修改,因為這樣很可能會引入新的bug。而要做到開閉原則,一般有2種途徑,一是通過繼承原有的類;二是通過抽象和接口。后面我們會拿書中的圖片加載框架來具體說明。
里氏替換原則
所有引用基類的地方必須能透明的使用其子類。通俗的說,就是只要父類能出現的地方子類就可以出現,而且替換為子類以后不會出現任何錯誤或異常。反過來就不行了,子類出現的地方父類不一定能適應。
- 要實現里氏替換原則,一般需要一個抽象的父類,父類中定義了子類的公共方法,子類繼承或是實現父類以后擴展不同的功能,這樣以來可以實現根據不同的需要來應用對應的子類,從而達到應用不同的功能的目的,程序的擴展性大大增強。同時這也體現了開閉原則,即當需要增加新功能時,只要繼承或實現父類,實現新增的功能就達到了擴展的目的,而不是直接修改原來的代碼,也即對擴展開放,對修改封閉。
依賴倒置原則
依賴倒置原則在Java中的表現就是:模塊間的依賴通過抽象發生,實現類之間不發生直接的依賴關系,其依賴關系是通過接口或抽象類產生的。
- 一句話就是依賴抽象而不依賴具體的實現。比如上面我們說開發一個圖片加載框架,那我們肯定會根據單一職責原則劃分不同的模塊,比如網絡加載模塊,圖片緩存模塊,以及主模塊等。主模塊肯定會調用圖片緩存模塊,如果我們調用的是圖片緩存模塊的具體實現,那么當我們修改圖片模塊時就很可能要對應修改主模塊,這就是耦合了。一個比較好的做法是將圖片緩存模塊抽象出來,而主模塊調用這個抽象即可,這樣也就是依賴抽象了。
接口隔離原則
類間的依賴關系應該建立在最小的接口上。
- 接口隔離原則就是讓客戶端依賴的接口盡可能的小。就是在上面提到的依賴倒置(依賴抽象而不是實現)原則的基礎上,增加一個最小化依賴的原則。說白了就是在依賴接口的基礎上依賴盡可能少的接口。
- 這里舉個例子:
<!--將圖片寫入SD卡-->
public void put(String url, Bitmap bitmap) {
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(SDPath+url);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
}catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
CloseUtils.closeQuietly(fileOutputStream);
}
}
<!--關閉工具類-->
public final class CloseUtils {
private CloseUtils() { }
/**
* 關閉Closeable對象
* @param closeable
*/
public static void closeQuietly(Closeable closeable) {
if (null != closeable) {
try {
closeable.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 上述例子中的CloseUtils的closeQuietly方法的原理就是依賴于Closeable抽象而不是具體實現(依賴倒置原則),并且建立在最小化依賴的基礎上,它只要知道這個對象是可以關閉的就行了,其他的一概不用關心,這就是接口隔離原則
迪米特原則
一個對象應該對其他的對象有最少的了解
- 通俗的講,一個類應該對自己需要耦合或調用的類知道得最少,調用者或是依賴者只要知道它需要的方法即可。要做到這個原則,需要我們對各個模塊之間的功能進行很好的區分和分配,把相互之間的依賴和耦合減到最少。
以上就是面向對象的六大原則。
下面我們通過書中的圖片加載框架ImageLoader的例子介紹這些原則的具體應用。
public class ImageLoader {
// 圖片緩存,依賴接口,而不是具體實現
// 如果改為MemoryCache mImageCache = new MemoryCache();就不能定制圖片緩存的實現,擴展性大大降低,耦合度也會大大提高
ImageCache mImageCache = new MemoryCache();
// 線程池,線程數量為CPU的數量
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
// 注入緩存,對擴展開放,對修改關閉
public void setImageCache(ImageCache cache) {
mImageCache = cache;
}
/**
* 顯示圖片
* @param imageUrl
* @param imageView
*/
public void displayImage(String imageUrl, ImageView imageView) {
Bitmap bitmap = mImageCache.get(imageUrl);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
// 圖片沒有緩存,提交到線程池下載
submitLoadRequest(imageUrl, imageView);
}
/**
* 下載圖片
* @param imageUrl
* @param imageView
*/
private void submitLoadRequest(final String imageUrl, final ImageView imageView) {
imageView.setTag(imageUrl);
mExecutorService.submit(new Runnable() {
@Override
public void run() {
Bitmap bitmap = downloadImage(imageUrl);
if (bitmap == null) {
return;
}
if (imageUrl.equals(imageView.getTag())) {
imageView.setImageBitmap(bitmap);
}
mImageCache.put(imageUrl, bitmap);
}
});
}
/**
* 下載圖片
* @param imageUrl
* @return
*/
private Bitmap downloadImage(String imageUrl) {
Bitmap bitmap = null;
try {
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
bitmap = BitmapFactory.decodeStream(connection.getInputStream());
connection.disconnect();
}catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
<!--圖片緩存接口-->
public interface ImageCache {
public Bitmap get(String url);
public void put(String url, Bitmap bitmap);
}
<!--內存緩存的實現-->
public class MemoryCache implements ImageCache{
private LruCache<String, Bitmap> mMemoryCache;
public MemoryCache() {
//初始化LRU緩存
initImageCache();
}
private void initImageCache() {
// 計算可使用的最大內存
final int maxMemory = (int) (Runtime.getRuntime().maxMemory()/1024);
// 取四分之一的可用內存作為緩存
final int cacheSize = maxMemory / 4;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
}
@Override
public Bitmap get(String url) {
return mMemoryCache.get(url);
}
@Override
public void put(String url, Bitmap bitmap) {
mMemoryCache.put(url, bitmap);
}
}
ImageLoader類中的邏輯比較直觀,六大原則的體現主要在圖片緩存的處理上。
- 將圖片緩存單獨出去而不是寫在ImageLoader一起,體現了單一職責原則
- ImageLoader中依賴的是圖片緩存的接口而不是具體的實現,體現了開閉原則、里氏替換原則和依賴倒置原則。
- ImageLoader類和MemoryCache類之間只依賴ImageCache接口,也可以說體現了接口隔離原則
- ImageLoader類只需要知道MemoryCache類的put方法和get方法,其他的實現一概不管,也體現了迪米特原則
當然以上的ImageLoader還只是初版,還有很多有待優化的地方,比如可以把下載的邏輯單獨出去,可以增加更多的定制功能等。
六大原則能讓我們的代碼結構更加合理,擴展性更好,讓程序更穩定靈活。這些原則往往都是共同作用的,比如上面例子中的CloseUtils滿足了單一職責原則,closeQuietly方法運用了依賴倒置,并且遵循接口隔離原則。趕緊用六大原則優化你的代碼吧!!!
最后編輯于 :
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。