服務(wù)項(xiàng)目:網(wǎng)站建設(shè)、仿站、程序開(kāi)發(fā)、APP開(kāi)發(fā)設(shè)計(jì)、移動(dòng)網(wǎng)站開(kāi)發(fā)設(shè)計(jì)、企業(yè)網(wǎng)站設(shè)計(jì)、電子商務(wù)網(wǎng)站開(kāi)發(fā)、網(wǎng)站維護(hù)、網(wǎng)站推廣、UX/UI 、HTML5、CSS3、JS / Jquery ...
四川???萍加邢薰?></a></div>
                    <div   id=四川浚??萍加邢薰? title=
四川浚??萍加邢薰?(開(kāi)發(fā)設(shè)計(jì)官網(wǎng))TEL : 15308000360 / QQ : 38585404

您的位置:首頁(yè) > 技術(shù)經(jīng)驗(yàn) > 編程開(kāi)發(fā) > 正文

SOLID原則是什么?
技術(shù)支持服務(wù)電話(huà):15308000360 【7x24提供運(yùn)維服務(wù),解決各類(lèi)系統(tǒng)/軟硬件疑難技術(shù)問(wèn)題】

S.O.L.I.D 是面向?qū)ο笤O(shè)計(jì)(OOD)和面向?qū)ο缶幊?OOP)中的幾個(gè)重要編碼原則(Programming Priciple)的首字母縮寫(xiě)。

 

面向?qū)ο笤O(shè)計(jì)的原則
SRP The Single Responsibility Principle 單一職責(zé)原則
OCP The Open Closed Principle 開(kāi)放封閉原則
LSP The Liskov Substitution Principle 里氏替換原則
ISP  The Interface Segregation Principle 接口分離原則
DIP The Dependency Inversion Principle 依賴(lài)倒置原則

 

一、單一職責(zé)原則(SRP)

從面向?qū)ο蠼嵌冉忉屵@個(gè)原則為:”引起類(lèi)變化的因素永遠(yuǎn)不要多于一個(gè)。” 或者說(shuō) “一個(gè)類(lèi)有且僅有一個(gè)職責(zé)”。這似乎不太好理解,特別是”引起類(lèi)變化的因素永遠(yuǎn)不要多于一個(gè)。”這句話(huà)更是有點(diǎn)虛,讓人有點(diǎn)摸不著頭腦。

我們通常都說(shuō)“低耦合,高內(nèi)聚”。在我看來(lái),這里的”單一職責(zé)”就是我們通常所說(shuō)的“高內(nèi)聚”,即一個(gè)類(lèi)只完成它應(yīng)該完成的職責(zé),不能推諉責(zé)任,也不可越殂代皰,不能成為無(wú)所不能的上帝類(lèi)。如果你的團(tuán)隊(duì)中實(shí)施寬松的“代碼集體所有權(quán)”,在編碼的過(guò)程中出現(xiàn)許多人同時(shí)修改(維護(hù))同一個(gè)類(lèi)的現(xiàn)象,而且成員之間的溝通不夠及時(shí),主動(dòng)和暢通的話(huà),那么時(shí)間一長(zhǎng),就很可能出現(xiàn)“承擔(dān)過(guò)多職責(zé)”的上帝類(lèi)。這時(shí),提煉基類(lèi)/接口和提煉類(lèi)重構(gòu)將能幫助我們消除或減輕這種設(shè)計(jì)臭味。

看一個(gè)例子:

這是一個(gè)違反了“單一職責(zé)原則” 的類(lèi)結(jié)構(gòu)圖。
這里,Rectangle類(lèi)做了下面兩件事:

  • 計(jì)算矩形面積;
  • 在界面(繪制設(shè)備)上繪制矩形;

并且,有兩個(gè)應(yīng)用使用了Rectangle類(lèi):

  • 計(jì)算幾何應(yīng)用程序(Computational Geometry Application)用這個(gè)類(lèi)計(jì)算面積;
  • 圖形程序(Graphical Application)用這個(gè)類(lèi)在界面上繪制矩形;

這違反了SRP(單一職責(zé)原則)。因?yàn)镽ectangle類(lèi)做了兩件事,在一個(gè)方法里它計(jì)算了面積,在另外一個(gè)方法了它返回一個(gè)表示矩形的GUI。這會(huì)帶來(lái)一些有趣的問(wèn)題:在計(jì)算幾何應(yīng)用程序中我們必須包含GUI。也就是在開(kāi)發(fā)幾何應(yīng)用時(shí),我們必須引用GUI庫(kù);圖形應(yīng)用程序中Rectangle類(lèi)的變化可能導(dǎo)致計(jì)算幾何應(yīng)用程序的變化,編譯和測(cè)試,反之亦然。那么,怎么修改才能讓其符合單一職責(zé)原則呢?

 

答案是:拆分!拆分職責(zé)到兩個(gè)不同的類(lèi)中,如:

  • Rectangle: 這個(gè)類(lèi)應(yīng)該只定義Area()方法;
  • RectangleUI: 這個(gè)類(lèi)應(yīng)繼承Rectangle類(lèi),并定義Draw()方法。

 

二、開(kāi)放封閉原則 (OCP)

從面向?qū)ο笤O(shè)計(jì)角度看,這個(gè)原則可以這么理解:”軟件實(shí)體(類(lèi),模塊,函數(shù)等等)應(yīng)當(dāng)對(duì)擴(kuò)展開(kāi)放,對(duì)修改閉合。” 通俗來(lái)講,它意味著你(或者類(lèi)的客戶(hù))應(yīng)當(dāng)能在不修改一個(gè)類(lèi)的前提下擴(kuò)展這個(gè)類(lèi)的行為。在OOD里,對(duì)擴(kuò)展開(kāi)放意味著類(lèi)或模塊的行為能夠改變,在需求變化時(shí)我們能以新的,不同的方式讓模塊改變,或者在新的應(yīng)用中滿(mǎn)足需求。

也就是說(shuō),對(duì)擴(kuò)展是開(kāi)放的,而對(duì)修改是封閉的。我們通常都說(shuō):向系統(tǒng)中增加功能時(shí)應(yīng)該只是添加新代碼,而應(yīng)該盡量少的修改原代碼。在我看來(lái),這就是遵循開(kāi)放封閉原則所能帶來(lái)的效果。曾經(jīng)在網(wǎng)上看到過(guò)這樣一句話(huà)“哪里變化,封裝哪里”。這其實(shí)就是說(shuō),我們要將系統(tǒng)中可能變化的地方封裝起來(lái),即對(duì)修改封閉。同時(shí),為了應(yīng)對(duì)系統(tǒng)需求(功能)的擴(kuò)展,需要抽象!

這里抽象是關(guān)鍵?!对O(shè)計(jì)模式》中的state模式和strategy模式是這個(gè)原則的最好體現(xiàn)。

舉一個(gè)例子:

違反了開(kāi)放封閉原則的類(lèi)結(jié)構(gòu)圖。

客戶(hù)端代碼直接面向服務(wù)器端的具體實(shí)現(xiàn)編程,缺乏靈活性。這樣如果服務(wù)器因?yàn)槟承┰虮黄渌?wù)器替換了,那么客戶(hù)端調(diào)用服務(wù)器的代碼也必須做相應(yīng)的修改或替換。這其實(shí)就是”面向?qū)崿F(xiàn)編程“的設(shè)計(jì)臭味!

那么,如何修改才能得到正確靈活的設(shè)計(jì)?

答案是:抽象!為服務(wù)器端的代碼(類(lèi)型)抽象出一個(gè)抽象基類(lèi)(定義一組完成服務(wù)職責(zé)的最小接口)。

下面是正確的設(shè)計(jì):

遵循開(kāi)放封閉原則的類(lèi)結(jié)構(gòu)圖。

基本上,你抽象的東西是你系統(tǒng)的核心內(nèi)容,如果你抽象得好,很可能增加一個(gè)新的服務(wù)器類(lèi)型(擴(kuò)展)只需要添加新類(lèi)型(繼承自AbstractServer即可)。因此代碼要盡可能以抽象(這里的AbstractServer)為依據(jù),這會(huì)允許你擴(kuò)展抽象事物,定義一個(gè)新的實(shí)現(xiàn)而不需要修改任何客戶(hù)端代碼。即”面向接口編程,不要面向?qū)崿F(xiàn)編程“!

三、Liskov’s 替換原則(LSP)

Liskov’s 替換原則意思是:”子類(lèi)型必須能夠替換它們的基類(lèi)型。”或者換個(gè)說(shuō)法:”使用基類(lèi)引用的地方必須能使用繼承類(lèi)的對(duì)象而不必知道它。” 這個(gè)原則正是保證繼承能夠被正確使用的前提。通常我們都說(shuō),“優(yōu)先使用組合(委托)而不是繼承”或者說(shuō)“只有在確定是 is-a 的關(guān)系時(shí)才能使用繼承”,因?yàn)槔^承經(jīng)常導(dǎo)致”緊耦合“的設(shè)計(jì)。
在基本的面向?qū)ο笤瓌t里,”繼承”通常是”is a”的關(guān)系。如果”Developer” 是一個(gè)”SoftwareProfessional”,那么”Developer”類(lèi)應(yīng)當(dāng)繼承”SoftwareProfessional”類(lèi)。在類(lèi)設(shè)計(jì)中”Is a”關(guān)系非常重要,但它容易沖昏頭腦,導(dǎo)致使用錯(cuò)誤的繼承造成錯(cuò)誤設(shè)計(jì)。

看一個(gè)最最經(jīng)典的例子:

遵循Liskov替換原則的類(lèi)結(jié)構(gòu)圖。

注:這里,KingFisher(翠鳥(niǎo))類(lèi)擴(kuò)展了Bird基類(lèi),并繼承了Fly()方法,這沒(méi)有問(wèn)題。

但是下面這個(gè)類(lèi)結(jié)構(gòu)圖就存在設(shè)計(jì)上的問(wèn)題:

違反Liskov替換原則的類(lèi)結(jié)構(gòu)圖。

Ostrich(鴕鳥(niǎo))是一種鳥(niǎo),這毋庸置疑,并從Bird類(lèi)繼承,這從概念上說(shuō)沒(méi)有問(wèn)題。但是鴕鳥(niǎo)它能飛嗎?不能,那么這個(gè)設(shè)計(jì)就違反了LSP。因?yàn)樵谑褂肂ird的地方不一定能用Ostrich代替。所以,即使在現(xiàn)實(shí)中看起來(lái)沒(méi)問(wèn)題,在類(lèi)設(shè)計(jì)中,Ostrich不應(yīng)該從Bird類(lèi)繼承,這里應(yīng)該從Bird中分離一個(gè)不會(huì)飛的類(lèi)NoFlyBrid,Ostrich應(yīng)該繼承這個(gè)不會(huì)飛的鳥(niǎo)類(lèi)NoFlyBrid。

 

為什么LSP如此重要?

  • 如果沒(méi)有LSP,類(lèi)繼承就會(huì)混亂;如果子類(lèi)作為一個(gè)參數(shù)傳遞給方法,將會(huì)出現(xiàn)未知行為;
  • 如果沒(méi)有LSP,適用與基類(lèi)的單元測(cè)試將不能成功用于測(cè)試子類(lèi);

四、接口分離原則(ISP)

這個(gè)原則的意思是”客戶(hù)端不應(yīng)該被迫依賴(lài)于它們不用的接口。” 也就是說(shuō),一個(gè)接口或者類(lèi)應(yīng)該擁有盡可能少的行為(那么,什么叫盡可能少?就是少到恰好能完成它自身的職責(zé)),這也是保證“軟件系統(tǒng)模塊的粒度盡可能少,以達(dá)到高度可重用的目的。

接口包含太多的方法會(huì)降低其可用性,像這種包含了無(wú)用方法的”胖接口”會(huì)增加類(lèi)之間的耦合。如果一個(gè)類(lèi)想實(shí)現(xiàn)該接口,那么它需要實(shí)現(xiàn)所有的方法,盡管有些對(duì)它來(lái)說(shuō)可能完全沒(méi)用,所以這樣做會(huì)在系統(tǒng)中引入不必要的復(fù)雜度,降低代碼的可維護(hù)性或魯棒性。

接口分離原則確保實(shí)現(xiàn)的接口有它們共同的職責(zé),它們是明確的,易理解的,可復(fù)用的.

下面這個(gè)例子充分的說(shuō)明了”接口應(yīng)該僅包含必要的方法,而不該包含其它的“。如果一個(gè)接口包含了過(guò)多的方法,應(yīng)該通過(guò)分離接口將其拆分。

這是一個(gè)違反接口分離原則的胖接口。

注意到IBird接口包含很多鳥(niǎo)類(lèi)的行為,包括Fly()行為.現(xiàn)在如果一個(gè)Bird類(lèi)(如Ostrich)實(shí)現(xiàn)了這個(gè)接口,那么它需要實(shí)現(xiàn)不必要的Fly()行為(Ostrich不會(huì)飛)。因此,這個(gè)”胖接口”應(yīng)該拆分成兩個(gè)不同的接口,IBird和IFlyingBird, 而IFlyingBird繼承自IBird。如下圖所示:

這樣的話(huà),重用將變得非常靈活:如果一種鳥(niǎo)不會(huì)飛(如Ostrich),那它實(shí)現(xiàn)IBird接口。如果一種鳥(niǎo)會(huì)飛(如KingFisher),那么它實(shí)現(xiàn)IFlyingBird。

 

因此,如果我們想要獲得可重用的方案,就應(yīng)當(dāng)遵循接口分離原則,把接口定義成僅包含必要的部分,以便在任何需要該接口功能的地方復(fù)用這個(gè)接口。

五、依賴(lài)倒置原則(DIP)

這個(gè)原則的意思是:高層模塊不應(yīng)該依賴(lài)底層模塊,兩者都應(yīng)該依賴(lài)其抽象。其實(shí)又是”面向接口編程,不要面向?qū)崿F(xiàn)編程“的內(nèi)在要求。

我們考慮一個(gè)現(xiàn)實(shí)中的例子,來(lái)看看依賴(lài)倒置原則給我們軟件帶來(lái)的好處。

你的汽車(chē)是由很多如引擎,車(chē)輪,空調(diào)和其它等部件組成,對(duì)嗎?

注意:這里的 Car 就是高層模塊;它依賴(lài)于抽象接口IToyotaEngine 和 IEighteenInchWheel.

而具體的引擎FifteenHundredCCEngine 屬于底層模塊,也依賴(lài)于抽象接口IToyotaEngine ;

具體的車(chē)輪 EighteenInchWheelWithAlloy同樣屬于底層模塊,也依賴(lài)于抽象接口IEighteenInchWheel。

 

上面Car類(lèi)有兩個(gè)屬性(引擎和車(chē)輪列表),它們都是抽象類(lèi)型(接口)。引擎和車(chē)輪是可插拔的,因?yàn)槠?chē)能接受任何實(shí)現(xiàn)了聲明接口的對(duì)象,并且Car類(lèi)不需要做任何改動(dòng)。

除SOLID原則外還有很多其它的面向?qū)ο笤瓌t。如:

 

  1. “組合替代繼承”:這是說(shuō)相對(duì)于繼承,要更傾向于使用組合;
  2. “笛米特法則”:這是說(shuō)”你的類(lèi)對(duì)其它類(lèi)知道的越少越好”;
  3. “共同封閉原則”:這是說(shuō)”相關(guān)類(lèi)應(yīng)該打包在一起”;
  4. “穩(wěn)定抽象原則”:這是說(shuō)”類(lèi)越穩(wěn)定,越應(yīng)該由抽象類(lèi)組成”;
        當(dāng)然,這些原則并不是孤立存在的,而是緊密聯(lián)系的,遵循一個(gè)原則的同時(shí)也就遵循了另外一個(gè)或多個(gè)原則;反之,違反了其中一個(gè)原則也很可能同時(shí)就違反了另外一個(gè)或多個(gè)原則。 設(shè)計(jì)模式是這些原則在一些特定場(chǎng)景的應(yīng)用結(jié)果。因此,可以把設(shè)計(jì)模式看作”框架”,把OOD原則看作”規(guī)范”。 在學(xué)習(xí)設(shè)計(jì)模式的過(guò)程中,我們要經(jīng)常性的反思,這個(gè)設(shè)計(jì)模式體現(xiàn)了面向?qū)ο笤O(shè)計(jì)原則中的哪個(gè)或哪一些原則。
      特別是在重構(gòu)實(shí)現(xiàn)模式,或重構(gòu)趨向模式的過(guò)程中,我們更要結(jié)合SOLID原則思考代碼在重構(gòu)前后的區(qū)別,理解它的改進(jìn)。



上一篇:3條必須知道的軟件開(kāi)發(fā)原則
下一篇:網(wǎng)站建設(shè)過(guò)程中性能優(yōu)化的34條經(jīng)驗(yàn)方法

相關(guān)熱詞搜索:solid