在日常開(kāi)發(fā)過(guò)程中時(shí)常需要用到設(shè)計(jì)模式,但是設(shè)計(jì)模式有23種述么,如何將這些設(shè)計(jì)模式了然于胸并且能在實(shí)際開(kāi)發(fā)過(guò)程中應(yīng)用得得心應(yīng)手呢?和我一起跟著《Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)》一書邊學(xué)邊應(yīng)用吧盗飒!
今天我們要講的是面向?qū)ο蟮牧笤瓌t
單一職責(zé)原則
就一個(gè)類而言置鼻,應(yīng)該僅有一個(gè)引起它變化的原因。簡(jiǎn)單來(lái)說(shuō)取逾,一個(gè)類中應(yīng)該是一組相關(guān)性很高的函數(shù)耗绿、數(shù)據(jù)的封裝。
- 我們?cè)贏pp中往往會(huì)用到很多的公共方法砾隅,比如獲取系統(tǒng)的時(shí)間误阻,這個(gè)功能可能在App中的很多地方都要用到。這個(gè)時(shí)候我們一般會(huì)單獨(dú)寫個(gè)工具類TimeUtils晴埂,把處理時(shí)間有關(guān)的方法都放到這個(gè)類里面究反,這樣就能減少重復(fù)代碼,App的結(jié)構(gòu)會(huì)更加清晰儒洛。當(dāng)需要添加其他跟時(shí)間有關(guān)的方法時(shí)精耐,就可以都加到這個(gè)TimeUtils類里面,這就是我們平時(shí)遵循的單一職責(zé)原則琅锻。
開(kāi)閉原則
軟件中的對(duì)象(類黍氮、模塊唐含、函數(shù)等),應(yīng)該對(duì)于擴(kuò)展是開(kāi)放的沫浆,而對(duì)于修改是封閉的捷枯。
- 我們?cè)谲浖_(kāi)發(fā)過(guò)程中就要考慮到后續(xù)的擴(kuò)展和修改。比如說(shuō)专执,我們?cè)陂_(kāi)發(fā)一款類似于universal-image-loader的圖片加載框架淮捆,可能一開(kāi)始我們的功能比較簡(jiǎn)單,圖片緩存只有內(nèi)存緩存本股。當(dāng)我們新版本需要添加SD卡緩存時(shí)攀痊,就要注意盡可能的減少對(duì)原來(lái)代碼的修改,因?yàn)檫@樣很可能會(huì)引入新的bug拄显。而要做到開(kāi)閉原則苟径,一般有2種途徑,一是通過(guò)繼承原有的類躬审;二是通過(guò)抽象和接口棘街。后面我們會(huì)拿書中的圖片加載框架來(lái)具體說(shuō)明。
里氏替換原則
所有引用基類的地方必須能透明的使用其子類承边。通俗的說(shuō)遭殉,就是只要父類能出現(xiàn)的地方子類就可以出現(xiàn),而且替換為子類以后不會(huì)出現(xiàn)任何錯(cuò)誤或異常博助。反過(guò)來(lái)就不行了险污,子類出現(xiàn)的地方父類不一定能適應(yīng)。
- 要實(shí)現(xiàn)里氏替換原則富岳,一般需要一個(gè)抽象的父類蛔糯,父類中定義了子類的公共方法,子類繼承或是實(shí)現(xiàn)父類以后擴(kuò)展不同的功能窖式,這樣以來(lái)可以實(shí)現(xiàn)根據(jù)不同的需要來(lái)應(yīng)用對(duì)應(yīng)的子類蚁飒,從而達(dá)到應(yīng)用不同的功能的目的,程序的擴(kuò)展性大大增強(qiáng)脖镀。同時(shí)這也體現(xiàn)了開(kāi)閉原則,即當(dāng)需要增加新功能時(shí)狼电,只要繼承或?qū)崿F(xiàn)父類蜒灰,實(shí)現(xiàn)新增的功能就達(dá)到了擴(kuò)展的目的,而不是直接修改原來(lái)的代碼肩碟,也即對(duì)擴(kuò)展開(kāi)放强窖,對(duì)修改封閉。
依賴倒置原則
依賴倒置原則在Java中的表現(xiàn)就是:模塊間的依賴通過(guò)抽象發(fā)生削祈,實(shí)現(xiàn)類之間不發(fā)生直接的依賴關(guān)系翅溺,其依賴關(guān)系是通過(guò)接口或抽象類產(chǎn)生的脑漫。
- 一句話就是依賴抽象而不依賴具體的實(shí)現(xiàn)。比如上面我們說(shuō)開(kāi)發(fā)一個(gè)圖片加載框架咙崎,那我們肯定會(huì)根據(jù)單一職責(zé)原則劃分不同的模塊优幸,比如網(wǎng)絡(luò)加載模塊,圖片緩存模塊褪猛,以及主模塊等网杆。主模塊肯定會(huì)調(diào)用圖片緩存模塊,如果我們調(diào)用的是圖片緩存模塊的具體實(shí)現(xiàn)伊滋,那么當(dāng)我們修改圖片模塊時(shí)就很可能要對(duì)應(yīng)修改主模塊碳却,這就是耦合了。一個(gè)比較好的做法是將圖片緩存模塊抽象出來(lái)笑旺,而主模塊調(diào)用這個(gè)抽象即可昼浦,這樣也就是依賴抽象了。
接口隔離原則
類間的依賴關(guān)系應(yīng)該建立在最小的接口上筒主。
- 接口隔離原則就是讓客戶端依賴的接口盡可能的小关噪。就是在上面提到的依賴倒置(依賴抽象而不是實(shí)現(xiàn))原則的基礎(chǔ)上,增加一個(gè)最小化依賴的原則物舒。說(shuō)白了就是在依賴接口的基礎(chǔ)上依賴盡可能少的接口色洞。
- 這里舉個(gè)例子:
<!--將圖片寫入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);
}
}
<!--關(guān)閉工具類-->
public final class CloseUtils {
private CloseUtils() { }
/**
* 關(guān)閉Closeable對(duì)象
* @param closeable
*/
public static void closeQuietly(Closeable closeable) {
if (null != closeable) {
try {
closeable.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 上述例子中的CloseUtils的closeQuietly方法的原理就是依賴于Closeable抽象而不是具體實(shí)現(xiàn)(依賴倒置原則),并且建立在最小化依賴的基礎(chǔ)上冠胯,它只要知道這個(gè)對(duì)象是可以關(guān)閉的就行了火诸,其他的一概不用關(guān)心,這就是接口隔離原則
迪米特原則
一個(gè)對(duì)象應(yīng)該對(duì)其他的對(duì)象有最少的了解
- 通俗的講荠察,一個(gè)類應(yīng)該對(duì)自己需要耦合或調(diào)用的類知道得最少置蜀,調(diào)用者或是依賴者只要知道它需要的方法即可。要做到這個(gè)原則悉盆,需要我們對(duì)各個(gè)模塊之間的功能進(jìn)行很好的區(qū)分和分配盯荤,把相互之間的依賴和耦合減到最少。
以上就是面向?qū)ο蟮牧笤瓌t焕盟。
下面我們通過(guò)書中的圖片加載框架ImageLoader的例子介紹這些原則的具體應(yīng)用秋秤。
public class ImageLoader {
// 圖片緩存,依賴接口脚翘,而不是具體實(shí)現(xiàn)
// 如果改為MemoryCache mImageCache = new MemoryCache();就不能定制圖片緩存的實(shí)現(xiàn)灼卢,擴(kuò)展性大大降低,耦合度也會(huì)大大提高
ImageCache mImageCache = new MemoryCache();
// 線程池来农,線程數(shù)量為CPU的數(shù)量
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
// 注入緩存,對(duì)擴(kuò)展開(kāi)放鞋真,對(duì)修改關(guān)閉
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;
}
// 圖片沒(méi)有緩存,提交到線程池下載
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);
}
<!--內(nèi)存緩存的實(shí)現(xiàn)-->
public class MemoryCache implements ImageCache{
private LruCache<String, Bitmap> mMemoryCache;
public MemoryCache() {
//初始化LRU緩存
initImageCache();
}
private void initImageCache() {
// 計(jì)算可使用的最大內(nèi)存
final int maxMemory = (int) (Runtime.getRuntime().maxMemory()/1024);
// 取四分之一的可用內(nèi)存作為緩存
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類中的邏輯比較直觀沃于,六大原則的體現(xiàn)主要在圖片緩存的處理上涩咖。
- 將圖片緩存單獨(dú)出去而不是寫在ImageLoader一起海诲,體現(xiàn)了單一職責(zé)原則
- ImageLoader中依賴的是圖片緩存的接口而不是具體的實(shí)現(xiàn),體現(xiàn)了開(kāi)閉原則檩互、里氏替換原則和依賴倒置原則特幔。
- ImageLoader類和MemoryCache類之間只依賴ImageCache接口,也可以說(shuō)體現(xiàn)了接口隔離原則
- ImageLoader類只需要知道MemoryCache類的put方法和get方法盾似,其他的實(shí)現(xiàn)一概不管敬辣,也體現(xiàn)了迪米特原則
當(dāng)然以上的ImageLoader還只是初版,還有很多有待優(yōu)化的地方零院,比如可以把下載的邏輯單獨(dú)出去溉跃,可以增加更多的定制功能等。
六大原則能讓我們的代碼結(jié)構(gòu)更加合理告抄,擴(kuò)展性更好撰茎,讓程序更穩(wěn)定靈活。這些原則往往都是共同作用的打洼,比如上面例子中的CloseUtils滿足了單一職責(zé)原則龄糊,closeQuietly方法運(yùn)用了依賴倒置,并且遵循接口隔離原則募疮。趕緊用六大原則優(yōu)化你的代碼吧l懦汀!阿浓!
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者