设计模式——设计模式之禅day1
單一職責(zé)
原則的定義是:應(yīng)該有且僅有一個(gè)原因引起類的變更。
單一職責(zé)原則有什么好處:
類的復(fù)雜性降低,實(shí)現(xiàn)什么職責(zé)都有清晰明確的定義;
可讀性提高,復(fù)雜性降低,那當(dāng)然可讀性提高了;
可維護(hù)性提高,可讀性提高,那當(dāng)然更容易維護(hù)了;
變更引起的風(fēng)險(xiǎn)降低,變更是必不可少的,如果接口的單一職責(zé)做得好,一個(gè)接口修改只對(duì)相應(yīng)的實(shí)現(xiàn)類有影響,對(duì)其他的接口無(wú)影響,這對(duì)系統(tǒng)的擴(kuò)展性、維護(hù)性都有非常大的幫助。
單一職責(zé)原則最難劃分的就是職責(zé)。一個(gè)職責(zé)一個(gè)接口,但問(wèn)題是“職責(zé)”沒(méi)有一個(gè)量化的標(biāo)準(zhǔn),一個(gè)類到底要負(fù)責(zé)那些職責(zé)?這些職責(zé)該怎么細(xì)化?細(xì)化后是否都要有一個(gè)接口或類?這些都需要從實(shí)際的項(xiàng)目去考慮.
對(duì)于接口,我們?cè)谠O(shè)計(jì)的時(shí)候一定要做到單一,但是對(duì)于實(shí)現(xiàn)類就需要多方面考慮了。生搬硬套單一職責(zé)原則會(huì)引起類的劇增,給維護(hù)帶來(lái)非常多的麻煩,而且過(guò)分細(xì)分類的職責(zé)也會(huì)人為地增加系統(tǒng)的復(fù)雜性。本來(lái)一個(gè)類可以實(shí)現(xiàn)的行為硬要拆成兩個(gè)類,然后再使用聚合或組合的方式耦合在一起,人為制造了系統(tǒng)的復(fù)雜性。所以原則是死的,人是活的,這句話很有道理。
單一職責(zé)適用于接口、類,同時(shí)也適用于方法,什么意思呢?一個(gè)方法盡可能做一件事情,比如一個(gè)方法修改用戶密碼,不要把這個(gè)方法放到“修改用戶信息”方法中。
對(duì)于單一職責(zé)原則,我的建議是接口一定要做到單一職責(zé),類的設(shè)計(jì)盡量做到只有一個(gè)原因引起變化。
注意: 單一職責(zé)原則提出了一個(gè)編寫程序的標(biāo)準(zhǔn),用“職責(zé)”或“變化原因”來(lái)衡量接口或類設(shè)計(jì)得是否優(yōu)良,但是“職責(zé)”和“變化原因”都是不可度量的,因項(xiàng)目而異,因環(huán)境而異。
里氏替換原則
在面向?qū)ο蟮恼Z(yǔ)言中,繼承是必不可少的、非常優(yōu)秀的語(yǔ)言機(jī)制,它有如下優(yōu)點(diǎn):
? 代碼共享,減少創(chuàng)建類的工作量,每個(gè)子類都擁有父類的方法和屬性;
? 提高代碼的重用性;
? 子類可以形似父類,但又異于父類,“龍生龍,鳳生鳳,老鼠生來(lái)會(huì)打洞”是說(shuō)子擁有父的“種”,“世界上沒(méi)有兩片完全相同的葉子”是指明子與父的不同;
? 提高代碼的可擴(kuò)展性,實(shí)現(xiàn)父類的方法就可以“為所欲為”了,君不見(jiàn)很多開(kāi)源框架的擴(kuò)展接口都是通過(guò)繼承父類來(lái)完成的;
? 提高產(chǎn)品或項(xiàng)目的開(kāi)放性。
自然界的所有事物都是優(yōu)點(diǎn)和缺點(diǎn)并存的,即使是雞蛋,有時(shí)候也能挑出骨頭來(lái),繼承的缺點(diǎn)如下:
? 繼承是侵入性的。只要繼承,就必須擁有父類的所有屬性和方法;
? 降低代碼的靈活性。子類必須擁有父類的屬性和方法,讓子類自由的世界中多了些約束;
? 增強(qiáng)了耦合性。當(dāng)父類的常量、變量和方法被修改時(shí),需要考慮子類的修改,而且在缺乏規(guī)范的環(huán)境下,這種修改可能帶來(lái)非常糟糕的結(jié)果—大段的代碼需要重構(gòu)。
定義:所有引用基類的地方必須能透明地使用其子類的對(duì)象。
1. 子類必須完全實(shí)現(xiàn)父類的方法
注意:在類中調(diào)用其他類時(shí)務(wù)必要使用父類或接口,如果不能使用父類或接口,則說(shuō)明類的設(shè)計(jì)已經(jīng)違背了LSP原則。
2. 子類可以有自己的個(gè)性
3. 覆蓋或?qū)崿F(xiàn)父類的方法時(shí)輸入?yún)?shù)可以被放大
子類在沒(méi)有覆寫父類的方法的前提下,子類方法被執(zhí)行了,這會(huì)引起業(yè)務(wù)邏輯混亂,因?yàn)樵趯?shí)際應(yīng)用中父類一般都是抽象類,子類是實(shí)現(xiàn)類,你傳遞一個(gè)這樣的實(shí)現(xiàn)類就會(huì)“歪曲”了父類的意圖,引起一堆意想不到的業(yè)務(wù)邏輯混亂,所以子類中方法的前置條件必須與超類中被覆寫的方法的前置條件相同或者更寬松。
注意:如果子類不能完整地實(shí)現(xiàn)父類的方法,或者父類的某些方法在子類中已經(jīng)發(fā)生“畸變”,則建議斷開(kāi)父子繼承關(guān)系,采用依賴、聚集、組合等關(guān)系代替繼承。
4. 覆寫或?qū)崿F(xiàn)父類的方法時(shí)輸出結(jié)果可以被縮小
這是什么意思呢,父類的一個(gè)方法的返回值是一個(gè)類型T,子類的相同方法(重載或覆寫)的返回值為S,那么里氏替換原則就要求S必須小于等于T,也就是說(shuō),要么S和T是同一個(gè)類型,要么S是T的子類,為什么呢?分兩種情況,如果是覆寫,父類和子類的同名方法的輸入?yún)?shù)是相同的,兩個(gè)方法的范圍值S小于等于T,這是覆寫的要求,這才是重中之重,子類覆寫父類的方法,天經(jīng)地義。如果是重載,則要求方法的輸入?yún)?shù)類型或數(shù)量不相同,在里氏替換原則要求下,就是子類的輸入?yún)?shù)寬于或等于父類的輸入?yún)?shù),也就是說(shuō)你寫的這個(gè)方法是不會(huì)被調(diào)用的,參考上面講的前置條件。
采用里氏替換原則的目的就是增強(qiáng)程序的健壯性,版本升級(jí)時(shí)也可以保持非常好的兼容性。即使增加子類,原有的子類還可以繼續(xù)運(yùn)行。在實(shí)際項(xiàng)目中,每個(gè)子類對(duì)應(yīng)不同的業(yè)務(wù)含義,使用父類作為參數(shù),傳遞不同的子類完成不同的業(yè)務(wù)邏輯,非常完美!
依賴倒置原則
包含三層含義:
? 高層模塊不應(yīng)該依賴低層模塊,兩者都應(yīng)該依賴其抽象;
? 抽象不應(yīng)該依賴細(xì)節(jié);
? 細(xì)節(jié)應(yīng)該依賴抽象。
高層模塊和低層模塊容易理解,每一個(gè)邏輯的實(shí)現(xiàn)都是由原子邏輯組成的,不可分割的原子邏輯就是低層模塊,原子邏輯的再組裝就是高層模塊。那什么是抽象?什么又是細(xì)節(jié)呢?在Java語(yǔ)言中,抽象就是指接口或抽象類,兩者都是不能直接被實(shí)例化的;細(xì)節(jié)就是實(shí)現(xiàn)類,實(shí)現(xiàn)接口或繼承抽象類而產(chǎn)生的類就是細(xì)節(jié),其特點(diǎn)就是可以直接被實(shí)例化,也就是可以加上一個(gè)關(guān)鍵字new產(chǎn)生一個(gè)對(duì)象。依賴倒置原則在Java語(yǔ)言中的表現(xiàn)就是:
? 模塊間的依賴通過(guò)抽象發(fā)生,實(shí)現(xiàn)類之間不發(fā)生直接的依賴關(guān)系,其依賴關(guān)系是通過(guò)接口或抽象類產(chǎn)生的;
? 接口或抽象類不依賴于實(shí)現(xiàn)類;
? 實(shí)現(xiàn)類依賴接口或抽象類。
更加精簡(jiǎn)的定義就是“面向接口編程” —OOD( Object-Oriented Design,面向?qū)ο笤O(shè)計(jì))的精髓之一。
采用依賴倒置原則可以減少類間的耦合性,提高系統(tǒng)的穩(wěn)定性,降低并行開(kāi)發(fā)引起的風(fēng)險(xiǎn),提高代碼的可讀性和可維護(hù)性。
注意:設(shè)計(jì)是否具備穩(wěn)定性,只要適當(dāng)?shù)亍八伤赏痢?#xff0c;觀察“設(shè)計(jì)的藍(lán)圖”是否還可以茁壯地成長(zhǎng)就可以得出結(jié)論,穩(wěn)定性較高的設(shè)計(jì),在周圍環(huán)境頻繁變化的時(shí)候,依然可以做到“我自巋然不動(dòng)”。
兩個(gè)類之間有依賴關(guān)系,只要制定出兩者之間的接口(或抽象類)就可以獨(dú)立開(kāi)發(fā)了,而且項(xiàng)目之間的單元測(cè)試也可以獨(dú)立地運(yùn)行,而TDD( Test-Driven Development,測(cè)試驅(qū)動(dòng)開(kāi)發(fā))開(kāi)發(fā)模式就是依賴倒置原則的最高級(jí)應(yīng)用。
抽象是對(duì)實(shí)現(xiàn)的約束,對(duì)依賴者而言,也是一種契約,不僅僅約束自己,還同時(shí)約束自己與外部的關(guān)系,其目的是保證所有的細(xì)節(jié)不脫離契約的范疇,確保約束雙方按照既定的契約(抽象)共同發(fā)展,只要抽象這根基線在,細(xì)節(jié)就脫離不了這個(gè)圈圈,始終讓你的對(duì)象做到“言必信,行必果”。
對(duì)象的依賴關(guān)系有三種方式來(lái)傳遞:
1. 構(gòu)造函數(shù)傳遞依賴對(duì)象
2. Setter方法傳遞依賴對(duì)象
3. 接口聲明依賴對(duì)象
在接口的方法中聲明依賴對(duì)象, 3.2節(jié)的例子就采用了接口聲明依賴的方式,該方法也叫做接口注入。
依賴倒置原則的本質(zhì)就是通過(guò)抽象(接口或抽象類)使各個(gè)類或模塊的實(shí)現(xiàn)彼此獨(dú)立,不互相影響,實(shí)現(xiàn)模塊間的松耦合,我們?cè)趺丛陧?xiàng)目中使用這個(gè)規(guī)則呢?只要遵循以下的幾個(gè)規(guī)則就可以:
? 每個(gè)類盡量都有接口或抽象類,或者抽象類和接口兩者都具備
這是依賴倒置的基本要求,接口和抽象類都是屬于抽象的,有了抽象才可能依賴倒置。
? 變量的表面類型盡量是接口或者是抽象類
很多書上說(shuō)變量的類型一定要是接口或者是抽象類,這個(gè)有點(diǎn)絕對(duì)化了,比如一個(gè)工具類,xxxUtils一般是不需要接口或是抽象類的。還有,如果你要使用類的clone方法,就必須使用實(shí)現(xiàn)類,這個(gè)是JDK提供的一個(gè)規(guī)范。
? 任何類都不應(yīng)該從具體類派生
如果一個(gè)項(xiàng)目處于開(kāi)發(fā)狀態(tài),確實(shí)不應(yīng)該有從具體類派生出子類的情況,但這也不是絕對(duì)的,因?yàn)槿硕际菚?huì)犯錯(cuò)誤的,有時(shí)設(shè)計(jì)缺陷是在所難免的,因此只要不超過(guò)兩層的繼承都是可以忍受的。特別是負(fù)責(zé)項(xiàng)目維護(hù)的同志,基本上可以不考慮這個(gè)規(guī)則,為什么?維護(hù)工作基本上都是進(jìn)行擴(kuò)展開(kāi)發(fā),修復(fù)行為,通過(guò)一個(gè)繼承關(guān)系,覆寫一個(gè)方法就可以修正一個(gè)很大的Bug,何必去繼承最高的基類呢?(當(dāng)然這種情況盡量發(fā)生在不甚了解父類或者無(wú)法獲得父類
代碼的情況下。)
? 盡量不要覆寫基類的方法
如果基類是一個(gè)抽象類,而且這個(gè)方法已經(jīng)實(shí)現(xiàn)了,子類盡量不要覆寫。類間依賴的是抽象,覆寫了抽象方法,對(duì)依賴的穩(wěn)定性會(huì)產(chǎn)生一定的影響。
? 結(jié)合里氏替換原則使用
在第2章中我們講解了里氏替換原則,父類出現(xiàn)的地方子類就能出現(xiàn),再結(jié)合本章的講解,我們可以得出這樣一個(gè)通俗的規(guī)則: 接口負(fù)責(zé)定義public屬性和方法,并且聲明與其他對(duì)象的依賴關(guān)系,抽象類負(fù)責(zé)公共構(gòu)造部分的實(shí)現(xiàn),實(shí)現(xiàn)類準(zhǔn)確的實(shí)現(xiàn)業(yè)務(wù)邏輯,同時(shí)在適當(dāng)?shù)臅r(shí)候?qū)Ω割愡M(jìn)行細(xì)化。
?依賴倒置原則是6個(gè)設(shè)計(jì)原則中最難以實(shí)現(xiàn)的原則,它是實(shí)現(xiàn)開(kāi)閉原則的重要途徑,依賴倒置原則沒(méi)有實(shí)現(xiàn),就別想實(shí)現(xiàn)對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。在項(xiàng)目中,大家只要記住是“面向接口編程”就基本上抓住了依賴倒置原則的核心。
轉(zhuǎn)載于:https://www.cnblogs.com/gpdm/p/5938633.html
總結(jié)
以上是生活随笔為你收集整理的设计模式——设计模式之禅day1的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C语言拼接字符串 -- 使用strcat
- 下一篇: Asp.Net Core--基于角色的授