接口隔离原则_设计模式六大原则
作者:VieLei
原文:https://blog.csdn.net/s10461/article/details/53941097
在android中我們常提起的設計模式大約有23種,利用好設計模式能夠幫助我們很好的構建代碼結構,也便于我們以后的維護。每次看完,看的時候總覺得很明白,但是過一段時間以后都會忘記,或只記得一些片段,又或到真正使用起來的時候才會發現一系列的問題。本系列筆記會從頭記錄這23種設計模式,當然一定會有不正確的地方。
本系列筆記參考了《設計模式之禪》、《Android源碼設計模式解析與實戰》以及huachao1001的 從Android代碼中來記憶23種設計模式一文。
開始之前
在所有的設計模式之前都會先將面向對象的六大原則,大部分同學都已經很明白了,所以在這里也想記錄一些不一樣的東西。
單一職責原則(SRP)
一個類所承擔的功能或指責必須是同一類或者相近的,不要讓一個類承擔過多的職責。換一句話說,一個類的職責應該單一。但是在我們的使用過程中,在大部分應用中很難做到真正意義上的指責單一,所以對于單一職責原則我是這么理解的:
接口的職責必須單一。
類的設計盡量做到只有一個原因引起類的變化,指的是設計類而不是實現類,實現類做到單一職責的話無形中會增加很多類,會使原本簡單的事情變得更加復雜。
開閉原則(OCP)
一個軟件實體應該對擴展開發,對修改關閉。其含義是說一個軟件實體應該通過擴展實現變化,而不是通過修改內部已有的代碼來實現變化。
軟件實體是什么?
項目或軟件產品中按照一定邏輯劃分的模塊
抽象和類
方法
開閉原則指導我們,當軟件需要變化時應”盡量通過”擴展的方式來修改,而不是通過變化已有代碼來實現,這里說的是盡量,并不是絕對不可以修改原始類,當我們嗅到“腐化”氣味時應盡量早重構。而不是通過繼承等方式添加新的實現,這會導致類的膨脹及歷史代碼的遺留。
通常我們可以用接口或抽象類來約束一組可變化的行為。主要包含3個層次:
第一:通過接口或抽象類約束擴展,對擴展邊界定義,不允許實現類出現接口或抽象類以外的public 方法。
第二:參數類型、引用變量盡量使用接口或抽象對類,而不是實現類。
第三:抽象層盡量保持穩定。
當然在實際應用過程中,往往修改源代碼和擴展是同時存在的。
里氏替換原則(LSP)
有兩種解釋方法,有一種特別麻煩、特別繞的就不再說了。
所有引用基類的地方必須能透明地使用其子類對象。
依然比較繞,通俗一點解釋是這樣的:只要任何有父類出現的地方(如形參),都可以替換為子類,而且替換為子類也不會產生異常和錯誤。對于使用者(方法)本身不需要關心到底是父類還是子類。
但是,有子類出現的地方,替換為父類就不一定可以了。
舉個例子:
//窗口類public?class?Window(){
????public?void?show(View?child){
????????child.draw();
????}
}
public?abstract?class?View(){
????public?abstract?void?draw();
????public?void?measure(int?widht,int?height){
????????//測量視圖大小
????}
}
public?class?Button?extends?View{
????public?void?draw(){
????????//繪制按鈕
????}
}
public?class?TextView?extends?View{
????public?void?draw(){
????????//繪制文本
????}
}
里氏替換原則為良好的繼承定義了一個規范:
1、子類必須完全實現父類的方法。我們在做系統設計的時候經常定義一個接口或抽象類,然后編碼實現,調用類直接傳入接口或抽象類,其實這里已經使用了里氏原則。
如果子類不能全實現父類的方法,或者父類的某些方法在子類中已經發生畸變,建議斷開繼承關系,采用依賴、聚合、組合等關系代替。
2、子類有自己的個性。即有子類出現的地方,父類未必可以。
3、覆蓋或實子類重載父類的方法時,傳入的參數必須比父類更寬松(相同或范圍大)。(否則會出現本來虛調用父類方法的地方調用了子類的方法)
4、覆蓋或實子類重載父類的方法時,返回的結果必須范圍更小(和父類類型相同或是父類返回類型的子類)。
依賴倒置原則(DIP)
依賴倒置主要是實現解耦,使得高層次的模塊不依賴于低層次模塊的具體實現細節。怎么去理解它呢,我們需要知道幾個關鍵點:
(1)高層模塊不應該依賴底層模塊(具體實現),二者都應該依賴其抽象(抽象類或接口)。模塊之間的依賴通過抽象產生,實現類之間不發生直接依賴關系,依賴關系通過接口或抽象類產生。
高層模塊就是調用端,底層模塊就是具體實現類。
(2)抽象不應該依賴細節(廢話,抽象類跟接口肯定不依賴具體的實現了)
(3)細節應該依賴于抽象(同樣廢話,具體實現類肯定要依賴其繼承的抽象類或接口)
在我們用的Java語言中,抽象就是指接口或者抽象類,二者都是不能直接被實例化;細節就是實現類,實現接口或者繼承抽象類而產生的類,就是細節。使用Java語言描述就簡單了:就是各個模塊之間相互傳遞的參數聲明為抽象類型,而不是聲明為具體的實現類;
依賴導致的本質是通過抽象使各個類或者模塊的實現彼此獨立,不相互影響,實現模塊間的松耦合。
每個類(底層模塊)盡量有接口和抽象類,或者接口抽象類二者皆有。
變量的聲明盡量是抽象或者接口
盡量不覆蓋父類的方法
里氏替換原則
一個例子:
母親給孩子講故事,只要給她一本書,她就可以照著書給孩子講故事了。代碼如下:
????public?String?getContent(){??
????????return?"很久很久以前有一個阿拉伯的故事……";??
????}??
}??
class?Mother{??
????public?void?narrate(Book?book){??
????????System.out.println("媽媽開始講故事");??
????????System.out.println(book.getContent());??
????}??
}??
public?class?Client{??
????public?static?void?main(String[]?args){??
????????Mother?mother?=?new?Mother();??
????????mother.narrate(new?Book());??
????}??
}??
運行結果:媽媽開始講故事 很久很久以前有一個阿拉伯的故事……
運行良好,假如有一天,需求變成這樣:不是給書而是給一份報紙,讓這位母親講一下報紙上的故事,報紙的代碼如下:
class?Newspaper{??????public?String?getContent(){??
????????return?"林書豪38+7領導尼克斯擊敗湖人……";??
????}??
}??
這位母親卻辦不到,因為她居然不會讀報紙上的故事,這太荒唐了,只是將書換成報紙,居然必須要修改Mother才能讀。假如以后需求換成雜志呢?換成網頁呢?還要不斷地修改Mother,這顯然不是好的設計。原因就是Mother與Book之間的耦合性太高了,必須降低他們之間的耦合度才行。
我們引入一個抽象的接口IReader。讀物,只要是帶字的都屬于讀物:
interface?IReader{??????public?String?getContent();??
}??
Mother類與接口IReader發生依賴關系,而Book和Newspaper都屬于讀物的范疇,他們各自都去實現IReader接口,這樣就符合依賴倒置原則了,代碼修改為:
class?Newspaper?implements?IReader?{??????public?String?getContent(){??
????????return?"林書豪17+9助尼克斯擊敗老鷹……";??
????}??
}??
class?Book?implements?IReader{??
????public?String?getContent(){??
????????return?"很久很久以前有一個阿拉伯的故事……";??
????}??
}??
class?Mother{??
????public?void?narrate(IReader?reader){??
????????System.out.println("媽媽開始講故事");??
????????System.out.println(reader.getContent());??
????}??
}??
public?class?Client{??
????public?static?void?main(String[]?args){??
????????Mother?mother?=?new?Mother();??
????????mother.narrate(new?Book());??
????????mother.narrate(new?Newspaper());??
????}??
}?????
這樣修改后,無論以后怎樣擴展Client類,都不需要再修改Mother類了。這只是一個簡單的例子,實際情況中,代表高層模塊的Mother類將負責完成主要的業務邏輯,一旦需要對它進行修改,引入錯誤的風險極大。所以遵循依賴倒置原則可以降低類之間的耦合性,提高系統的穩定性,降低修改程序造成的風險。
依賴倒置原則在Java中的表現就是:模塊間通過抽象發生,實現類之間不發生直接依賴關系,其依賴關系是通過接口或者抽象類產生的。如果類與類直接依賴細節,那么就會直接耦合,那么當修改時,就會同時修改依賴者代碼,這樣限制了可擴展性。
接口隔離原則(ISP)
關于接口有一點要說明:類也是接口
有兩種定義方法:一種為類不應該依賴它不需要的接口;另一種為類之間的依賴關系應該建立在最小的接口上。接口隔離的原則是將非常龐大、臃腫的接口分割成更小更具體的接口。
有的時候會感覺單一職責和接口隔離隔離原則很像,單一職責更關注的是功能的單一,是業務層次上的劃分。而接口隔離原則更關心的是接口的數量要少。
比如說一個接口有一組10個功能,提供給若干個模塊使用,每個模塊按照按照不同的權限只能使用接口中的一部分功能。按照單一職責原則是允許的,但是按照接口隔離原則是不允許的,接口隔離原則要求:”盡量使用多個專用接口”,意思為有幾個模塊就提供幾個接口,而不是建立一個龐大的接口供所有模塊使用。
接口設計時有限度的,當然也不要分了隔離接口而將全部接口都細化,這樣就變為過度設計了
最佳實踐:
一個接口只服務于一個模塊或業務流程
壓縮接口對外提供的public方法
迪米特原則(LOD)
一個對象應該對一個被調用的類(被耦合)有最少的了解。調用者只需要知道它需要調用的方法即可。類與類之間的關系越密切,當一個類發生改變時,對另一個類影響也越大。
迪米特原則包含的四層含義:
只和朋友交流:每個類必然會和其他類有耦合關系,兩個對象間的耦合就成為朋友關系(聚合、組合、依賴等)。朋友類得定義是這樣的:出現在成員變量、方法的輸入、輸出參數中的稱為成員朋友類,而出現在方法體內部的類不屬于朋友類。
朋友之間也是有距離的:不要對外公布太多的public方法和非靜態的public變量,盡量內斂。
迪米特原則的核心觀念是:解耦。即:高內聚、低耦合。
最后
單一職責原則 (SRP)
開閉原則 (OCP)
里氏替換原則 (LSP)
迪米特原則 (LOD)
接口隔離原則 (ISP)
依賴倒置原則 (DIP)
至此設計模式的6大基礎原則幾經全部介紹完畢,將6大原則的首字母組合起來,就是SOLID(穩定的)。
不是說按照一定的設計模式來設計程序就能應對各種場景,或不對代碼結構做任何修改。只是我們在進行程序設計的時候盡量遵循這6大原則。
長按關注,更多精彩
看完,有收獲就點個“好看”鴨
↓↓↓↓
總結
以上是生活随笔為你收集整理的接口隔离原则_设计模式六大原则的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python离群点检测例子_异常点/离群
- 下一篇: 中学生可以这样学python.pdf_中