
1.單一職責(zé)原則(Single Responsibility Principle,縮寫(xiě)SRP)
單一職責(zé)原則,就一個(gè)類(lèi)而言,應(yīng)該只有一個(gè)引起它變化的原因。簡(jiǎn)單說(shuō),一個(gè)類(lèi)應(yīng)該是一組高度相關(guān)的函數(shù)、數(shù)據(jù)的封裝;也就是高內(nèi)聚。
下面代碼為 ImageLoader(圖片加載)類(lèi)的代碼
public class ImageLoader{
//圖片緩存
LruCache<String,Bitmap> mImageCache;
//線程池,線程數(shù)量為CPU的數(shù)量
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProessors());
public ImageLoader(){
initImageCache();
}
private void initImageCache() {
//省略...
}
//顯示圖片
public void displayImage(final String url, final ImageView imageView) {
//省略...
}
//下載圖片
public Bitmap downloadImage(String imageUrl) {
//省略...
return bitmap;
}
}
這里可以看出來(lái) ImageLoader 類(lèi)作用有初始化圖片緩存、顯示圖片、下載圖片,顯然顯示圖片和下載圖片兩個(gè)方法與初始化圖片緩存方法相比作用就顯得有些不相關(guān)。也就是不符合單一職責(zé)原則。按照邏輯進(jìn)行分拆之后得到ImageLoader和ImageCache兩個(gè)類(lèi)。ImageLoader負(fù)責(zé)圖片加載邏輯,ImageCache負(fù)責(zé)處理圖片緩存邏輯,這樣職責(zé)就清楚了,當(dāng)與緩存相關(guān)的邏輯需要改變時(shí),不需要修改ImageLoader類(lèi),而圖片加載的邏輯需要修改時(shí)也不會(huì)影響到緩存處理邏輯。
ImageLoader代碼修改如下所示:
/** 圖片加載類(lèi) */
public class ImageLoader {
//圖片緩存
ImageCache mImageCache = new ImageCache() ;
//線程池,線程數(shù)量為CPU的數(shù)量
ExecutorService mExecutorService = Executors.newFixedThreadPool (Runtime.getRuntime().availableProcessors());
//加載圖片
public void displayImage(final String url, final ImageView imageView) {
//省略...
}
public Bitmap downloadImage(String imageUrl) {
//省略...
return bitmap;
}
}
而添加的ImageCache類(lèi)用于處理圖片緩存,具體代碼如下:
public class ImageCache {
// 圖片LRU緩存
LruCache<String, Bitmap> mImageCache;
public ImageCache() {
initImageCache();
}
private void initImageCache() {
//省略...
}
public void put(String url, Bitmap bitmap) {
mImageCache.put(url, bitmap) ;
}
public Bitmap get(String url) {
return mImageCache.get(url) ;
}
}
如何劃分一個(gè)類(lèi)、一個(gè)函數(shù)的職責(zé),每個(gè)人都有自己的看法,這需要根據(jù)個(gè)人經(jīng)驗(yàn)、具體的業(yè)務(wù)邏輯而定。
2.開(kāi)閉原則(Open Close Principle,縮寫(xiě)OCP)
開(kāi)閉原則是Java中最基礎(chǔ)的設(shè)計(jì)原則,知道我們?nèi)绾谓⒁粋€(gè)穩(wěn)定的、靈活的系統(tǒng)。定義:軟件中得對(duì)象應(yīng)該對(duì)于擴(kuò)展是開(kāi)放的,但是對(duì)于修改是封閉的。

例如圖中MemonyCache、DiskCache、DoubleCache都實(shí)現(xiàn)了ImageCache接口,ImageLoader使用ImageCache處理緩存,就意味著ImageLoader可以通過(guò)setImageCache()指定使用哪一種緩存類(lèi)型,可以使三種緩存其中任意一種,同時(shí)不需要修改ImageLoader中的代碼。這也就是開(kāi)閉原則的體現(xiàn)。
簡(jiǎn)單地說(shuō),當(dāng)軟件需要變化時(shí),應(yīng)該盡量通過(guò)擴(kuò)展的方式來(lái)實(shí)現(xiàn)變化,而不是通過(guò)修改已有的代碼來(lái)實(shí)現(xiàn)?!皯?yīng)該盡量”4個(gè)字說(shuō)明OCP原則并不是說(shuō)絕對(duì)不可以修改原始類(lèi)的,當(dāng)代碼需要需要重構(gòu)的時(shí)候要及時(shí)重構(gòu),使代碼恢復(fù)正常,而不是通過(guò)繼承等方式添加新的實(shí)現(xiàn),這會(huì)導(dǎo)致類(lèi)型的膨脹以及歷史遺留代碼的冗余。
開(kāi)發(fā)過(guò)程中都沒(méi)有那么理想的狀況,因此,凡事也是需要結(jié)合具體情況再做決定,目的是更穩(wěn)定、更靈活同時(shí)保有原有的正確性。
3.里氏替換原則(Liskov Substitution Principle,縮寫(xiě)LSP)
里氏替換原則,書(shū)上原話的定義簡(jiǎn)直看不得(解釋的辣眼睛,完全看不懂),簡(jiǎn)單地說(shuō)就是所有引用基類(lèi)的地方必須能透明地使用其子類(lèi)的對(duì)象。只要父類(lèi)能出現(xiàn)的地方子類(lèi)就可以出現(xiàn),而且替換為子類(lèi)也不會(huì)產(chǎn)生任何錯(cuò)誤或異常,使用者可能根本就不需要知道是父類(lèi)還是子類(lèi)。但是,反過(guò)來(lái)就不行了,有子類(lèi)出現(xiàn)的地方,父類(lèi)未必就能適應(yīng)。其實(shí)就是:抽象。

上圖可以看出,Window依賴(lài)于View,而Button和TextView繼承View。這里任何繼承自View類(lèi)的子類(lèi)都可以設(shè)置給show()方法,也就是里氏替換原則。通過(guò)里氏替換,就可以自定義各式各樣的View,然后傳遞給Window,并且將View顯示到屏幕上。
里氏替換原則的核心原理是抽象,抽象又依賴(lài)于繼承這個(gè)特性,繼承的優(yōu)缺點(diǎn)都相當(dāng)明
優(yōu)點(diǎn):
- 代碼重用,減少創(chuàng)建類(lèi)的成本,每個(gè)子類(lèi)都擁有父類(lèi)的方法和屬性
- 子類(lèi)與父類(lèi)基本相似,但又與父類(lèi)有所區(qū)別
- 提高代碼的可擴(kuò)展性
缺點(diǎn):
- 繼承是侵入性的,只要繼承就必須擁有父類(lèi)的所有屬性和方法
- 可能造成子類(lèi)代碼冗余、靈活性降低,因?yàn)樽宇?lèi)必須擁有父類(lèi)的屬性和方法
事物總是具有兩面性,如何權(quán)衡利與弊都是需要根據(jù)具體場(chǎng)景來(lái)做出選擇并加以處理。
4.依賴(lài)倒置原則(Dependence Inversion Principle,縮寫(xiě)DIP)
依賴(lài)倒置原則,說(shuō)的就是一種特定的就形式,使得高層次的模塊不依賴(lài)于低層次的模塊的實(shí)現(xiàn)細(xì)節(jié)的目的,依賴(lài)模塊被顛倒了。依賴(lài)倒置原則的幾個(gè)關(guān)鍵點(diǎn):
- 高層模塊不應(yīng)該依賴(lài)低層模塊,兩者都應(yīng)該依賴(lài)其抽象
- 抽象不應(yīng)該依賴(lài)細(xì)節(jié)
- 細(xì)節(jié)應(yīng)該依賴(lài)抽象
是不是覺(jué)得和沒(méi)說(shuō)一個(gè)樣,至少我是這么覺(jué)得的;繼續(xù)往后看才明白,所謂高層模塊就是調(diào)用端,低層模塊就是具體實(shí)現(xiàn)類(lèi)。依賴(lài)倒置原則在 Java 語(yǔ)言中的表現(xiàn)就是:模塊間的依賴(lài)通過(guò)抽象發(fā)生,實(shí)現(xiàn)類(lèi)之間不發(fā)生直接的依賴(lài)關(guān)系,通過(guò)接口或抽象類(lèi)產(chǎn)生依賴(lài)關(guān)系。什么是依賴(lài)關(guān)系呢?其實(shí)就是相互之間的調(diào)用關(guān)系。概括來(lái)說(shuō)就是面向接口變成,或者是面向抽象編程。
其實(shí)依賴(lài)倒置原則主要目的就是解耦

依然可以使用這張圖來(lái)表示,表達(dá)出來(lái)就是ImageLoader和MemonyCache等并沒(méi)有直接關(guān)系,甚至ImageLoader只需要實(shí)現(xiàn)ImageCache類(lèi)或繼承其他已有的ImageCache子類(lèi)完成相應(yīng)的緩存功能,然后將具體的實(shí)現(xiàn)注入到ImageLoader即可實(shí)現(xiàn)緩存功能的替換。這也是依賴(lài)倒置原則的體現(xiàn)。
5.接口隔離原則(Interface Segregation Principle,縮寫(xiě)ISP)
接口隔離原則將非常龐大、臃腫的接口拆分成為更小的和更具體的接口;目的就是解耦。這個(gè)原則的做法和單一職責(zé)原則有點(diǎn)相似,就是說(shuō)接口中得方法保持更高的相關(guān)性、盡量少,避免掉不需要的方法。
舉個(gè)栗子,現(xiàn)在有一個(gè)帶有呼吸方法的接口,還有一個(gè)打鼾方法的接口;如果說(shuō),你把這兩個(gè)方法放到一個(gè)接口中,基本就是違背接口隔離原則,畢竟呼吸和打鼾沒(méi)有什么緊密的相關(guān)性;不可能說(shuō)我需要呼吸的時(shí)候一定需要打鼾吧!
6.迪米特原則(Law of Principle,縮寫(xiě)LOP)
迪米特原則也稱(chēng)為最少知識(shí)原則(Least Knowledge Principle),定義:一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象有最少的了解。通俗地講,一個(gè)類(lèi)要對(duì)自己需要調(diào)用的類(lèi)知道得最少,類(lèi)的內(nèi)部如何實(shí)現(xiàn)、如何復(fù)雜都與調(diào)用者(或依賴(lài)者)沒(méi)關(guān)系,調(diào)用者(或依賴(lài)者)只需要知道他需要的方法即可,其他的不需要關(guān)心。類(lèi)與類(lèi)之間的關(guān)系越密切,耦合度越大;當(dāng)一個(gè)類(lèi)發(fā)生改變時(shí),對(duì)另一個(gè)類(lèi)的影響也越大。
迪米特法則還有一個(gè)英文解釋是:Only talk to your immedate friends,翻譯過(guò)來(lái)就是:只與直接的朋友通信。什么叫做直接的朋友呢?每個(gè)對(duì)象都必然會(huì)與其他對(duì)象有耦合關(guān)系,兩個(gè)對(duì)象之間的耦合就成為朋友關(guān)系,這種關(guān)系的類(lèi)型有很多,例如組合、聚合、依賴(lài)等。
下圖是Volley框架中的DiskBasedCache類(lèi)(實(shí)現(xiàn)Cache接口)和Cache接口的代碼


Volley中的Response緩存接口的設(shè)計(jì)。Cache接口定義了緩存類(lèi)需要實(shí)現(xiàn)的最小接口,依賴(lài)緩存類(lèi)的對(duì)象只需要知道這些接口即可。例如緩存的具體實(shí)現(xiàn)類(lèi)DiskBasedCache,該緩存類(lèi)將Response序列化到本地,這就需要操作File以及相關(guān)的類(lèi)。
Volley的直接朋友就是DiskBasedCache,間接朋友就是mRootDirectory、mEntries等。Volley只需要直接和Cache類(lèi)交互即可,并不需要知道File、mEntries等對(duì)象的存在。
文中有引用書(shū)本中得例子,也有根據(jù)自己理解舉的例子,如有不對(duì)還望指出。