(single responsibility principle, srp):就乙個類而言,應該僅有乙個引起它變化的原因。
【問題】
類t負責兩個不同的職責:職責p1,職責p2。當由於職責p1需求發生改變需要修改類t時,有可能會導致原本執行正常的職責p2功能發生故障。
【解決方案】
遵循單一職責原則。分別建立兩個類t1、t2,使t1完成職責p1功能,t2完成職責p2功能。這樣,當修改t1時,不會使職責p2發生故障的風險。同理,當修改t2時,也不會使職責p1發生故障風險。
【優點】
可以降低類的複雜度,乙個類只負責一項職責,其邏輯肯定要比負責多項職責簡單的多。
提高類的可讀性,提高系統的可維護性。
變更引起的風險降低,變更是必然的,如果單一職責原則遵守的好,當修改乙個功能時,可以顯著降低對其他功能的影響。
(liskov substitution principle,lsp):子型別必須能夠替換掉它們的父型別。
父類中凡是已經實現好的方法(相對於抽象方法而言),實際上是在設定一系列的規範和契約,雖然它不強制要求所有的子類必須遵從這些契約,但是如果子類對這些非抽象方法任意修改,就會對整個繼承體系造成破壞。而黎克特制替換原則就是表達了這一層含義。
【問題】
有一功能p1,由類a完成,現在需要將功能p1進行擴充套件,擴充套件後的功能為p,其中p由原有功能p1與新功能p2組成。新功能p由類a的子類b來完成,則子類b在完成新功能p2的同時,有可能會導致原有功能p1發生故障。
【解決方案】
當使用繼承時,遵循黎克特制替換原則。類b繼承類a時,除新增新的方法完成新增功能p2外,盡量不要重寫父類a的方法,也盡量不要過載父類a的方法。
黎克特制代換原則是對「開-閉」原則的補充。實現「開-閉」原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,所以黎克特制代換原則是對實現抽象化的具體步驟的規範。
(dependency inversion principle,dip):高層模組不應該依賴低層模組,兩個都應該依賴抽象。抽象不應該依賴細節,細節應該依賴抽象。
【問題】
類a直接依賴類b,假如要將類a改為依賴類c,則必須通過修改類a的**來達成。這種場景下,類a一般是高層模組,負責複雜的業務邏輯;類b和類c是低層模組,負責基本的原子操作;假如修改類a,會給程式帶來不必要的風險。
【解決方案】
將類a修改為依賴介面i,類b和類c各自實現介面i,類a通過介面i間接與類b或者類c發生聯絡,則會大大降低修改類a的機率。
依賴倒轉原則要求我們在程式中要依賴於抽象介面,不要依賴於具體實現。程式**中傳遞引數時或在關聯關係中,盡量引用層次高的抽象層類,即使用介面和抽象類進行變數型別宣告、引數型別宣告、方法返回型別宣告,以及資料型別的轉換等,而不要用具體類來做這些事情。為了確保該原則的應用,乙個具體類應當只實現介面或抽象類中宣告過的方法,而不要給出多餘的方法,否則將無法呼叫到在子類中增加的新方法。
(inte***ce segregation principle,isp):使用多個專門的介面,而不使用單一的總介面,即客戶端不應該依賴那些它不需要的介面。
【問題】
類a通過介面i依賴類b,類c通過介面i依賴類d,如果介面i對於類a和類b來說不是最小介面,則類b和類d必須去實現他們不需要的方法。
這個圖的意思是:類a依賴介面i中的方法1、方法2、方法3,類b是對類a依賴的實現。類c依賴介面i中的方法1、方法4、方法5,類d是對類c依賴的實現。對於類b和類d來說,雖然他們都存在著用不到的方法(也就是圖中紅色字型標記的方法),但由於實現了介面i,所以也必須要實現這些用不到的方法。
在使用介面隔離原則時,我們需要注意控制介面的粒度,介面不能太小,如果太小會導致系統中介面氾濫,不利於維護;介面也不能太大,太大的介面將違背介面隔離原則,靈活性較差,使用起來很不方便。一般而言,介面中僅包含為某一類使用者定製的方法即可,不應該強迫客戶依賴於那些它們不用的方法。
(law of demeter, lod):如果兩個類不必彼此直接通訊,那麼這兩個類就不應當直接的相互作用。如果其中乙個類需要呼叫另乙個類的某乙個方法的話,可以直接通過第三者**這個呼叫。
【問題】
類與類之間的關係越密切,耦合度越大,當乙個類發生改變時,對另乙個類的影響也越大。
【解決方案】
盡量降低類與類之間的耦合。
迪公尺特法則又叫最少知道原則,最早是在2023年由美國northeastern university的ian holland提出。通俗的來講,就是乙個類對自己依賴的類知道的越少越好。也就是說,對於被依賴的類來說,無論邏輯多麼複雜,都盡量地的將邏輯封裝在類的內部,對外除了提供的public方法,不對外洩漏任何資訊。迪公尺特法則還有乙個更簡單的定義:只與直接的朋友通訊。首先來解釋一下什麼是直接的朋友:每個物件都會與其他物件有耦合關係,只要兩個物件之間有耦合關係,我們就說這兩個物件之間是朋友關係。耦合的方式很多,依賴、關聯、組合、聚合等。其中,我們稱出現成員變數、方法引數、方法返回值中的類為直接的朋友,而出現在區域性變數中的類則不是直接的朋友。也就是說,陌生的類最好不要作為區域性變數的形式出現在類的內部。因此,應用迪公尺特法則有可能造成的乙個後果就是:系統中存在大量的中介類,這些類之所以存在完全是為了傳遞類之間的相互呼叫關係——這在一定程度上增加了系統的複雜度。
(open-closed principle,ocp):是說軟體實體(類、模組、函式等等)應該卡伊擴充套件,但是不可修改。
【問題】
在軟體的生命週期內,因為變化、公升級和維護等原因需要對軟體原有**進行修改時,可能會給舊**中引入錯誤,也可能會使我們不得不對整個功能進行重構,並且需要原有**經過重新測試。
【解決方案】
當軟體需要變化時,盡量通過擴充套件軟體實體的行為來實現變化,而不是通過修改已有的**來實現變化。
為了滿足開閉原則,需要對系統進行抽象化設計,抽象化是開閉原則的關鍵。無須對抽象層進行任何改動,只需要增加新的具體類來實現新的業務功能即可,實現在不修改已有**的基礎上擴充套件系統的功能,達到開閉原則的要求。使得軟體系統在擁有適應性和靈活性的同時具備較好的穩定性和延續性。
(composite reuse principle, carp):盡量使用合成/聚合,盡量不要使用類繼承。
【問題】
軟體公司開發人員在初期的系統設計中,考慮到客戶數量不多,系統採用mysql作為資料庫,與資料庫操作有關的a類等都需要連線資料庫,連線資料庫的方法封裝在b類中,由於需要重用b類的方法,設計人員將a作為b類的子類,隨著客戶數量的增加,系統決定公升級為oracle資料庫,因此需要增加乙個新的c類來連線oracle資料庫,由於在初始設計方案中a和b之間是繼承關係,因此在更換資料庫連線方式時需要修改a類的源**,將a作為c的子類,這將違反開閉原則。
【解決方案】
a和b之間的關係由繼承關係變為關聯關係,採用依賴注入的方式將b物件注入到a中,可以使用構造注入,也可以使用setter注入。如果需要對b的功能進行擴充套件,可以通過其子類來實現,如通過子類c來連線oracle資料庫。由於a針對b程式設計,根據黎克特制代換原則,b子類的物件可以覆蓋b物件,只需在a中注入子類物件即可使用子類所擴充套件的方法。例如在a中注入c物件,即可實現oracle資料庫連線,原有**無須進行修改,而且還可以很靈活地增加新的資料庫連線方式。
合成/聚合復用原則就是在乙個新的物件裡面使用一些已有的物件,使之成為新物件的一部分;新的物件通過向這些物件的委派達到復用已有功能的目的。
說了這麼多,我來舉個栗子
乙個非常生動的栗子
設計模式六大原則
0.05 設計模式 設計模式 規範 筆記 大話設計模式 物件導向的關鍵在於封裝,封裝好了才能很好的復用,達到單一職責和開放擴充套件 封閉更改的效果。1 單一職責原則 就乙個類而言,應該僅有乙個引起它變化的原因.增加功能不應該修改已有的 避免修改出錯及重複測試.如果你能夠想到多於乙個的動機去改變乙個類...
設計模式六大原則
0.05 設計模式 設計模式 規範 筆記 大話設計模式 物件導向的關鍵在於封裝,封裝好了才能很好的復用,達到單一職責和開放擴充套件 封閉更改的效果。1 單一職責原則 就乙個類而言,應該僅有乙個引起它變化的原因.增加功能不應該修改已有的 避免修改出錯及重複測試.如果你能夠想到多於乙個的動機去改變乙個類...
設計模式六大原則
參考文章 單一職責原則 single responsibility principle,srp 乙個類只負責乙個功能領域中的相應職責,或者可以定義為 就乙個類而言,應該只有乙個引起它變化的原因。開閉原則 open closed principle,ocp 乙個軟體實體應當對擴充套件開放,對修改關閉。...