SOLID設(shè)計(jì)原則

SOLID

分別為:

  • 單一職責(zé)原則
  • 開閉原則
  • 里氏替換原則
  • 接口隔離原則
  • 依賴倒置原則

單一職責(zé)原則

1. 如何理解單一職責(zé)原則(SRP)玫荣?

一個(gè)類只負(fù)責(zé)完成一個(gè)職責(zé)或者功能捅厂。不要設(shè)計(jì)大而全的類资柔,要設(shè)計(jì)粒度小、功能單一的類辙芍。單一職責(zé)原則是為了實(shí)現(xiàn)代碼高內(nèi)聚沸手、低耦合注簿,提高代碼的復(fù)用性跳仿、可讀性、可維護(hù)性妄辩。

2. 如何判斷類的職責(zé)是否足夠單一眼耀?

不同的應(yīng)用場(chǎng)景、不同階段的需求背景干花、不同的業(yè)務(wù)層面楞黄,對(duì)同一個(gè)類的職責(zé)是否單一鬼廓,可能會(huì)有不同的判定結(jié)果。實(shí)際上尤慰,一些側(cè)面的判斷指標(biāo)更具有指導(dǎo)意義和可執(zhí)行性雷蹂,比如萎河,出現(xiàn)下面這些情況就有可能說明這類的設(shè)計(jì)不滿足單一職責(zé)原則:

  • 類中的代碼行數(shù)、函數(shù)或者屬性過多玛歌;
  • 類依賴的其他類過多支子,或者依賴類的其他類過多达舒;
  • 私有方法過多;比較難給類起一個(gè)合適的名字昨登;
  • 類中大量的方法都是集中操作類中的某幾個(gè)屬性贯底。

3. 類的職責(zé)是否設(shè)計(jì)得越單一越好?

單一職責(zé)原則通過避免設(shè)計(jì)大而全的類飘哨,避免將不相關(guān)的功能耦合在一起琐凭,來(lái)提高類的內(nèi)聚性统屈。
同時(shí),類職責(zé)單一囤采,類依賴的和被依賴的其他類也會(huì)變少惩淳,減少了代碼的耦合性思犁,以此來(lái)實(shí)現(xiàn)代碼的高內(nèi)聚、低耦合棉磨。
但是学辱,如果拆分得過細(xì)策泣,實(shí)際上會(huì)適得其反,反倒會(huì)降低內(nèi)聚性统抬,也會(huì)影響代碼的可維護(hù)性聪建。

開閉原則

1. 如何理解“對(duì)擴(kuò)展開放茫陆、對(duì)修改關(guān)閉”盅弛?

添加一個(gè)新的功能叔锐,應(yīng)該是通過在已有代碼基礎(chǔ)上擴(kuò)展代碼(新增模塊见秽、類解取、方法返顺、屬性等)遂鹊,而非修改已有代碼(修改模塊、類慧邮、方法误澳、屬性等)的方式來(lái)完成秦躯。
關(guān)于定義踱承,我們有兩點(diǎn)要注意。

  • 第一點(diǎn)是毡琉,開閉原則并不是說完全杜絕修改桅滋,而是以最小的修改代碼的代價(jià)來(lái)完成新功能的開發(fā)身辨。
  • 第二點(diǎn)是,同樣的代碼改動(dòng)号俐,在粗代碼粒度下吏饿,可能被認(rèn)定為“修改”;在細(xì)代碼粒度下贞远,可能又被認(rèn)定為“擴(kuò)展”蓝仲。

2. 如何做到“對(duì)擴(kuò)展開放官疲、修改關(guān)閉”途凫?

我們要時(shí)刻具備擴(kuò)展意識(shí)、抽象意識(shí)果元、封裝意識(shí)噪漾。
在寫代碼的時(shí)候且蓬,我們要多花點(diǎn)時(shí)間思考一下恶阴,這段代碼未來(lái)可能有哪些需求變更,如何設(shè)計(jì)代碼結(jié)構(gòu)焦匈,事先留好擴(kuò)展點(diǎn)缓熟,以便在未來(lái)需求變更的時(shí)候摔笤,在不改動(dòng)代碼整體結(jié)構(gòu)吕世、做到最小代碼改動(dòng)的情況下,將新的代碼靈活地插入到擴(kuò)展點(diǎn)上况毅。

里氏替換原則

子類對(duì)象能夠替換程序中父類對(duì)象出現(xiàn)的任何地方尔许,并且保證原來(lái)程序的邏輯行為不變及正確性不被破壞母债。
里式替換原則是用來(lái)指導(dǎo)尝抖,繼承關(guān)系中子類該如何設(shè)計(jì)的一個(gè)原則。
理解里式替換原則衙熔,最核心的就是理解“design by contract红氯,按照協(xié)議來(lái)設(shè)計(jì)”這幾個(gè)字咕痛。
父類定義了函數(shù)的“約定”(或者叫協(xié)議)茉贡,那子類可以改變函數(shù)的內(nèi)部實(shí)現(xiàn)邏輯腔丧,但不能改變函數(shù)原有的“約定”。
這里的約定包括:函數(shù)聲明要實(shí)現(xiàn)的功能砾医;對(duì)輸入如蚜、輸出影暴、異常的約定错邦;甚至包括注釋中所羅列的任何特殊說明。

和多態(tài)的區(qū)別

雖然從定義描述和代碼實(shí)現(xiàn)上來(lái)看坤检,多態(tài)和里式替換有點(diǎn)類似兴猩,但它們關(guān)注的角度是不一樣的。
多態(tài)是面向?qū)ο缶幊痰囊淮筇匦栽缧彩敲嫦驅(qū)ο缶幊陶Z(yǔ)言的一種語(yǔ)法倾芝。它是一種代碼實(shí)現(xiàn)的思路讨勤。
而里式替換是一種設(shè)計(jì)原則,是用來(lái)指導(dǎo)繼承關(guān)系中子類該如何設(shè)計(jì)的晨另,子類的設(shè)計(jì)要保證在替換父類的時(shí)候,不改變?cè)谐绦虻倪壿嬕约安黄茐脑谐绦虻恼_性借尿。

接口隔離原則

1. 如何理解“接口隔離原則”刨晴?

理解“接口隔離原則”的重點(diǎn)是理解其中的“接口”二字。
這里有三種不同的理解路翻。

  • 如果把“接口”理解為一組接口集合狈癞,可以是某個(gè)微服務(wù)的接口,也可以是某個(gè)類庫(kù)的接口等茂契。如果部分接口只被部分調(diào)用者使用蝶桶,我們就需要將這部分接口隔離出來(lái),單獨(dú)給這部分調(diào)用者使用掉冶,而不強(qiáng)迫其他調(diào)用者也依賴這部分不會(huì)被用到的接口真竖。
  • 如果把“接口”理解為單個(gè) API 接口或函數(shù),部分調(diào)用者只需要函數(shù)中的部分功能厌小,那我們就需要把函數(shù)拆分成粒度更細(xì)的多個(gè)函數(shù)恢共,讓調(diào)用者只依賴它需要的那個(gè)細(xì)粒度函數(shù)。
  • 如果把“接口”理解為 OOP 中的接口璧亚,也可以理解為面向?qū)ο缶幊陶Z(yǔ)言中的接口語(yǔ)法讨韭。那接口的設(shè)計(jì)要盡量單一,不要讓接口的實(shí)現(xiàn)類和調(diào)用者涨岁,依賴不需要的接口函數(shù)拐袜。

2. 接口隔離原則與單一職責(zé)原則的區(qū)別

單一職責(zé)原則針對(duì)的是模塊、類梢薪、接口的設(shè)計(jì)蹬铺。
接口隔離原則相對(duì)于單一職責(zé)原則,一方面更側(cè)重于接口的設(shè)計(jì)秉撇,另一方面它的思考角度也是不同的甜攀。
接口隔離原則提供了一種判斷接口的職責(zé)是否單一的標(biāo)準(zhǔn):通過調(diào)用者如何使用接口來(lái)間接地判定。如果調(diào)用者只使用部分接口或接口的部分功能琐馆,那接口的設(shè)計(jì)就不夠職責(zé)單一规阀。

依賴反轉(zhuǎn)

控制反轉(zhuǎn)、依賴注入瘦麸、依賴反轉(zhuǎn)谁撼。

1. 控制反轉(zhuǎn)

實(shí)際上,控制反轉(zhuǎn)是一個(gè)比較籠統(tǒng)的設(shè)計(jì)思想滋饲,并不是一種具體的實(shí)現(xiàn)方法厉碟,一般用來(lái)指導(dǎo)框架層面的設(shè)計(jì)喊巍。這里所說的“控制”指的是對(duì)程序執(zhí)行流程的控制,而“反轉(zhuǎn)”指的是在沒有使用框架之前箍鼓,程序員自己控制整個(gè)程序的執(zhí)行崭参。在使用框架之后,整個(gè)程序的執(zhí)行流程通過框架來(lái)控制款咖。流程的控制權(quán)從程序員“反轉(zhuǎn)”給了框架何暮。

2. 依賴注入

依賴注入和控制反轉(zhuǎn)恰恰相反,它是一種具體的編碼技巧铐殃。我們不通過 new 的方式在類內(nèi)部創(chuàng)建依賴類的對(duì)象海洼,而是將依賴的類對(duì)象在外部創(chuàng)建好之后,通過構(gòu)造函數(shù)背稼、函數(shù)參數(shù)等方式傳遞(或注入)給類來(lái)使用贰军。

3. 依賴注入框架

我們通過依賴注入框架提供的擴(kuò)展點(diǎn)玻蝌,簡(jiǎn)單配置一下所有需要的類及其類與類之間依賴關(guān)系蟹肘,就可以實(shí)現(xiàn)由框架來(lái)自動(dòng)創(chuàng)建對(duì)象、管理對(duì)象的生命周期俯树、依賴注入等原本需要程序員來(lái)做的事情帘腹。

4. 依賴反轉(zhuǎn)原則

依賴反轉(zhuǎn)原則也叫作依賴倒置原則。這條原則跟控制反轉(zhuǎn)有點(diǎn)類似许饿,主要用來(lái)指導(dǎo)框架層面的設(shè)計(jì)阳欲。高層模塊不依賴低層模塊,它們共同依賴同一個(gè)抽象陋率。抽象不要依賴具體實(shí)現(xiàn)細(xì)節(jié)球化,具體實(shí)現(xiàn)細(xì)節(jié)依賴抽象。

KISS瓦糟、YAGNI

KISS

Keep It Simple and Stupid.
Keep It Short and Simple.
Keep It Simple and Straightforward.

KISS 原則是保持代碼可讀和可維護(hù)的重要手段筒愚。
KISS 原則中的“簡(jiǎn)單”并不是以代碼行數(shù)來(lái)考量的。代碼行數(shù)越少并不代表代碼越簡(jiǎn)單菩浙,我們還要考慮邏輯復(fù)雜度巢掺、實(shí)現(xiàn)難度、代碼的可讀性等劲蜻。而且陆淀,本身就復(fù)雜的問題,用復(fù)雜的方法解決先嬉,并不違背 KISS 原則轧苫。
除此之外,同樣的代碼疫蔓,在某個(gè)業(yè)務(wù)場(chǎng)景下滿足 KISS 原則含懊,換一個(gè)應(yīng)用場(chǎng)景可能就不滿足了钾军。對(duì)于如何寫出滿足 KISS 原則的代碼,我還總結(jié)了下面幾條指導(dǎo)原則:

  • 不要使用同事可能不懂的技術(shù)來(lái)實(shí)現(xiàn)代碼绢要;
  • 不要重復(fù)造輪子吏恭,要善于使用已經(jīng)有的工具類庫(kù);
  • 不要過度優(yōu)化重罪。

YAGNI

You Ain’t Gonna Need It.

不要去設(shè)計(jì)當(dāng)前用不到的功能樱哼;不要去編寫當(dāng)前用不到的代碼。
實(shí)際上剿配,這條原則的核心思想就是:不要做過度設(shè)計(jì)搅幅。

DRY

1.DRY 原則

Don't repeat yourself.

不要寫重復(fù)的代碼。

三種代碼重復(fù)的情況:

  • 實(shí)現(xiàn)邏輯重復(fù)呼胚、
  • 功能語(yǔ)義重復(fù)茄唐、
  • 代碼執(zhí)行重復(fù)。

還有文檔注釋重復(fù)蝇更、數(shù)據(jù)結(jié)構(gòu)重復(fù)沪编。兩種
實(shí)現(xiàn)邏輯重復(fù),但功能語(yǔ)義不重復(fù)的代碼年扩,并不違反 DRY 原則蚁廓。
實(shí)現(xiàn)邏輯不重復(fù),但功能語(yǔ)義重復(fù)的代碼厨幻,也算是違反 DRY 原則相嵌。
除此之外,代碼執(zhí)行重復(fù)也算是違反 DRY 原則况脆。

2. 代碼復(fù)用性

減少代碼耦合

對(duì)于高度耦合的代碼饭宾,當(dāng)我們希望復(fù)用其中的一個(gè)功能,想把這個(gè)功能的代碼抽取出來(lái)成為一個(gè)獨(dú)立的模塊格了、類或者函數(shù)的時(shí)候看铆,往往會(huì)發(fā)現(xiàn)牽一發(fā)而動(dòng)全身。移動(dòng)一點(diǎn)代碼笆搓,就要牽連到很多其他相關(guān)的代碼性湿。所以,高度耦合的代碼會(huì)影響到代碼的復(fù)用性满败,我們要盡量減少代碼耦合肤频。

滿足單一職責(zé)原則

我們前面講過,如果職責(zé)不夠單一算墨,模塊宵荒、類設(shè)計(jì)得大而全,那依賴它的代碼或者它依賴的代碼就會(huì)比較多,進(jìn)而增加了代碼的耦合报咳。根據(jù)上一點(diǎn)侠讯,也就會(huì)影響到代碼的復(fù)用性。相反暑刃,越細(xì)粒度的代碼厢漩,代碼的通用性會(huì)越好,越容易被復(fù)用岩臣。

模塊化

這里的“模塊”溜嗜,不單單指一組類構(gòu)成的模塊,還可以理解為單個(gè)類架谎、函數(shù)炸宵。我們要善于將功能獨(dú)立的代碼,封裝成模塊谷扣。獨(dú)立的模塊就像一塊一塊的積木土全,更加容易復(fù)用,可以直接拿來(lái)搭建更加復(fù)雜的系統(tǒng)会涎。

業(yè)務(wù)與非業(yè)務(wù)邏輯分離

越是跟業(yè)務(wù)無(wú)關(guān)的代碼越是容易復(fù)用裹匙,越是針對(duì)特定業(yè)務(wù)的代碼越難復(fù)用。所以在塔,為了復(fù)用跟業(yè)務(wù)無(wú)關(guān)的代碼幻件,我們將業(yè)務(wù)和非業(yè)務(wù)邏輯代碼分離,抽取成一些通用的框架蛔溃、類庫(kù)、組件等篱蝇。

通用代碼下沉

從分層的角度來(lái)看贺待,越底層的代碼越通用、會(huì)被越多的模塊調(diào)用零截,越應(yīng)該設(shè)計(jì)得足夠可復(fù)用麸塞。一般情況下,在代碼分層之后涧衙,為了避免交叉調(diào)用導(dǎo)致調(diào)用關(guān)系混亂哪工,我們只允許上層代碼調(diào)用下層代碼及同層代碼之間的調(diào)用,杜絕下層代碼調(diào)用上層代碼弧哎。所以雁比,通用的代碼我們盡量下沉到更下層。

繼承撤嫩、多態(tài)偎捎、抽象、封裝

在講面向?qū)ο筇匦缘臅r(shí)候,我們講到茴她,利用繼承寻拂,可以將公共的代碼抽取到父類,子類復(fù)用父類的屬性和方法丈牢。利用多態(tài)祭钉,我們可以動(dòng)態(tài)地替換一段代碼的部分邏輯,讓這段代碼可復(fù)用己沛。除此之外朴皆,抽象和封裝,從更加廣義的層面泛粹、而非狹義的面向?qū)ο筇匦缘膶用鎭?lái)理解的話遂铡,越抽象、越不依賴具體的實(shí)現(xiàn)晶姊,越容易復(fù)用扒接。代碼封裝成模塊会放,隱藏可變的細(xì)節(jié)附迷、暴露不變的接口,就越容易復(fù)用境输。

應(yīng)用模板等設(shè)計(jì)模式

一些設(shè)計(jì)模式蒙挑,也能提高代碼的復(fù)用性宗侦。比如,模板模式利用了多態(tài)來(lái)實(shí)現(xiàn)忆蚀,可以靈活地替換其中的部分代碼矾利,整個(gè)流程模板代碼可復(fù)用。

我們?cè)诘谝淮螌懘a的時(shí)候馋袜,如果當(dāng)下沒有復(fù)用的需求男旗,而未來(lái)的復(fù)用需求也不是特別明確,并且開發(fā)可復(fù)用代碼的成本比較高欣鳖,那我們就不需要考慮代碼的復(fù)用性察皇。
在之后開發(fā)新的功能的時(shí)候,發(fā)現(xiàn)可以復(fù)用之前寫的這段代碼泽台,那我們就重構(gòu)這段代碼什荣,讓其變得更加可復(fù)用。相比于代碼的可復(fù)用性怀酷,DRY 原則適用性更強(qiáng)一些稻爬。我們可以不寫可復(fù)用的代碼,但一定不能寫重復(fù)的代碼胰坟。

迪米特法則

最少知道原則因篇。

1. 如何理解“高內(nèi)聚泞辐、松耦合”?

“高內(nèi)聚竞滓、松耦合”是一個(gè)非常重要的設(shè)計(jì)思想咐吼,能夠有效提高代碼的可讀性和可維護(hù)性,縮小功能改動(dòng)導(dǎo)致的代碼改動(dòng)范圍商佑。
“高內(nèi)聚”用來(lái)指導(dǎo)類本身的設(shè)計(jì)锯茄,“松耦合”用來(lái)指導(dǎo)類與類之間依賴關(guān)系的設(shè)計(jì)。
所謂高內(nèi)聚茶没,就是指相近的功能應(yīng)該放到同一個(gè)類中肌幽,不相近的功能不要放到同一類中。相近的功能往往會(huì)被同時(shí)修改抓半,放到同一個(gè)類中喂急,修改會(huì)比較集中。
所謂松耦合指的是笛求,在代碼中廊移,類與類之間的依賴關(guān)系簡(jiǎn)單清晰。即使兩個(gè)類有依賴關(guān)系探入,一個(gè)類的代碼改動(dòng)也不會(huì)或者很少導(dǎo)致依賴類的代碼改動(dòng)狡孔。

2. 如何理解“迪米特法則”?

不該有直接依賴關(guān)系的類之間蜂嗽,不要有依賴苗膝;有依賴關(guān)系的類之間,盡量只依賴必要的接口植旧。
迪米特法則是希望減少類之間的耦合辱揭,讓類越獨(dú)立越好。每個(gè)類都應(yīng)該少了解系統(tǒng)的其他部分隆嗅。一旦發(fā)生變化界阁,需要了解這一變化的類就會(huì)比較少。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末胖喳,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子贮竟,更是在濱河造成了極大的恐慌丽焊,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咕别,死亡現(xiàn)場(chǎng)離奇詭異技健,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)惰拱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門雌贱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說我怎么就攤上這事欣孤〔雒唬” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵降传,是天一觀的道長(zhǎng)篷朵。 經(jīng)常有香客問我,道長(zhǎng)婆排,這世上最難降的妖魔是什么声旺? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮段只,結(jié)果婚禮上腮猖,老公的妹妹穿的比我還像新娘。我一直安慰自己赞枕,他們只是感情好澈缺,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鹦赎,像睡著了一般谍椅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上古话,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天雏吭,我揣著相機(jī)與錄音,去河邊找鬼陪踩。 笑死杖们,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肩狂。 我是一名探鬼主播摘完,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼傻谁!你這毒婦竟也來(lái)了孝治?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤审磁,失蹤者是張志新(化名)和其女友劉穎谈飒,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體态蒂,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡杭措,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了钾恢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片手素。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鸳址,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出泉懦,到底是詐尸還是另有隱情稿黍,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布祠斧,位于F島的核電站闻察,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏琢锋。R本人自食惡果不足惜辕漂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吴超。 院中可真熱鬧钉嘹,春花似錦、人聲如沸鲸阻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鸟悴。三九已至陈辱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間细诸,已是汗流浹背沛贪。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留震贵,地道東北人利赋。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像猩系,于是被迫代替她去往敵國(guó)和親媚送。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容