Java 中面向對象編程六大原則:
單一職責原則 英文名稱是Single Responsibility Principle,簡稱SRP
開閉原則 英文全稱是Open Close Principle,簡稱OCP
里氏替換原則 英文全稱是Liskov Substitution Principle,簡稱LSP
依賴倒置原則 英文全稱是Dependence Inversion Principle,簡稱DIP
接口隔離原則 英文全稱是InterfaceSegregation Principles,簡稱ISP
迪米特原則 英文全稱爲Law of Demeter,簡稱LOD,也稱爲最少知識原則(Least Knowledge Principle)
系統有更高的靈活性——接口隔離原則
接口隔離原則英文全稱是InterfaceSegregation Principles,簡稱ISP。它的定義是:客戶端不應該依賴它不需要的接口。另一種定義是:類間的依賴關係應該建立在最小的接口上。接口隔離原則將非常龐大、臃腫的接口拆分成爲更小的和更具體的接口,這樣客戶將會只需要知道他們感興趣的方法。接口隔離原則的目的是系統解開耦合,從而容易重構、更改和重新部署。
//將下載的圖片緩存到sd卡里
@Override
public void put(String key, Bitmap bitmap){
if(!mCachDir.exists()){
return;
}
//對傳入的key值進行MD5處理
String savedFilePath = mCacheDirPath+File.separator+hashKeyForDisk(key);
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(new File(savedFilePath));
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
finally {
if(fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
我們看到的這段代碼可讀性非常差,各種try…catch嵌套,都是些簡單的代碼,但是會嚴重影響代碼的可讀性,並且多層級的大括號很容易將代碼寫到錯誤的層級中。大家應該對這類代碼也非常反感,那我們看看如何解決這類問題。 我們可能知道Java中有一個Closeable接口,該接口標識了一個可關閉的對象,它只有一個close方法。如下圖:
既然這些文件流都是實現了Closeable接口,那隻要建一個方法統一來關閉這些對象不就可以了麼,代碼如下:
public class CloseUtils {
private CloseUtils(){}
public static void close(Closeable closeable){
try {
if(closeable != null) {
closeable.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
我們再看看把這段代碼運用到上述的put方法中的效果如何:
//將下載的圖片緩存到sd卡里
@Override
public void put(String key, Bitmap bitmap){
if(!mCachDir.exists()){
return;
}
//對傳入的key值進行MD5處理
String savedFilePath = mCacheDirPath+File.separator+hashKeyForDisk(key);
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(new File(savedFilePath));
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
finally {
CloseUtils.close(fileOutputStream);
}
}
代碼簡潔了很多!而且這個closeUtils方法可以運用到各類可關閉的對象中,保證了代碼的重用性。CloseUtils的close方法的基本原理就是依賴於Closeable抽象而不是具體實現(這不是前面講的依賴倒置原則麼),並且建立在最小化依賴原則的基礎,它只需要知道這個對象是可關閉,其他的一概不關心,也就是這裏的接口隔離原則。
試想一下,如果在只是需要關閉一個對象時,它卻暴露出了其他的接口函數,比如OutputStream的write方法,這就使得更多的細節暴露在客戶端代碼面前,不僅沒有很好地隱藏實現,還增加了接口的使用難度。而通過Closeable接口將可關閉的對象抽象起來,這樣只需要客戶端依賴於Closeable就可以對客戶端隱藏其他的接口信息,客戶端代碼只需要知道這個對象可關閉(只可調用close方法)即可。
更好的可擴展性——迪米特原則
迪米特原則英文全稱爲Law of Demeter,簡稱LOD,也稱爲最少知識原則(Least Knowledge Principle)。雖然名字不同,但描述的是同一個原則:一個對象應該對其他對象有最少的瞭解。通俗地講,一個類應該對自己需要耦合或調用的類知道得最少,類的內部如何實現、如何複雜都與調用者或者依賴者沒關係,調用者或者依賴者只需要知道他需要的方法即可,其他的我一概不關心。類與類之間的關係越密切,耦合度越大,當一個類發生改變時,對另一個類的影響也越大。
迪米特法則還有一個英文解釋是:Only talk to your immedate friends,翻譯過來就是:只與直接的朋友通信。什麼叫做直接的朋友呢?每個對象都必然會與其他對象有耦合關係,兩個對象之間的耦合就成爲朋友關係,這種關係的類型有很多,例如組合、聚合、依賴等。
我們來說個例子說明怎麼做到只和朋友交流。 說是有這麼一個故事,老師想讓班長確認一下全班女生來齊沒有,就對他說:“你去把全班女生清 一下。”班長沒聽清楚,或者是當時腦子正在回憶什麼東西,就問道:“親哪個?”老師¥#……¥%。 我們來看這個笑話怎麼用程序來實現:
/**
* 老師類
*/
public class Teacher {
//老師對班長髮布命令, 清一下女生
public void commond(Monitor monitor){
List<Girl> listGirls = new ArrayList() ;
//初始化女生
for(int i=0;i<20;i++){
listGirls.add(new Girl());
}
//告訴體育委員開始執行清查任務
monitor.countGirls(listGirls);
}
}
/**
* 班長
*/
public class Monitor {
//有清查女生的工作
public void countGirls(List<Girl> listGirls){
System.out.println(" 女生數量是: "+listGirls.size());
}
}
/**
* 女生
*/
public class Girl {
}
下面是模擬命令過程:
/**
* 調用類
*/
public class Client {
public static void main(String[] args) {
Teacher teacher = new Teacher();
//老師發佈命令
teacher.commond(new Monitor());
}
}
運行Client的結果就是:女生數量是:20
我們回過頭來看這個程序有什麼問題,首先來看 Teacher 有幾個朋友,就一個Monitor類,這個就是朋友類, 迪米特法則要求一個類只和朋友類交流, 但是 commond 方法中我們與 Girl 類有了交流,聲明瞭一個 List動態數組,也就是與一個陌生的類 Girl 有了交流,這樣設計不好,耦合嚴重,修改時很容易出錯。下面看看重新設計Teacher和Monitor類:
/**
* 老師類
*/
public class Teacher {
//老師對班長髮布命令, 清一下女生
public void commond(Monitor monitor){
//告訴體育委員開始執行清查任務
monitor.countGirls();
}
}
/**
* 班長
*/
public class Monitor {
//有清查女生的工作
public void countGirls(){
List<Girl> listGirls = new ArrayList() ;
//初始化女生
for(int i=0;i<20;i++){
listGirls.add(new Girl());
}
System.out.println(" 女生數量是: "+listGirls.size());
}
}
/**
* 女生
*/
public class Girl {
}
/**
* 調用類
*/
public class Client {
public void main(String[] args) {
Teacher teacher = new Teacher();
//老師發佈命令
teacher.commond(new Monitor());
}
}
程序做了一個簡單的修改,就是把 Teacher 中的對女生類 List初始化(這個是有業務意義的,產生出 全班的所有人員)移動到了 Monitor 的 countGrils 方法中,避開了 Teacher 類對陌生類 Girl 的訪問, 減少系統間的耦合, 使得系統具有更低的耦合性和更好的可擴展性。在我們之前的ImageLoader代碼裏面,ImageLoader只與ImageCahe交流,而具體實現ImageCahe的各個Cache類可以有自己不同的實現細節,但是這些細節對ImageLoader來說都是不可見的,這裏也是用到了迪米特原則。