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

您的位置:首頁 > 技術經驗 > 編程開發(fā) > 正文

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

S.O.L.I.D 是面向對象設計(OOD)和面向對象編程(OOP)中的幾個重要編碼原則(Programming Priciple)的首字母縮寫。

 

面向對象設計的原則
SRP The Single Responsibility Principle 單一職責原則
OCP The Open Closed Principle 開放封閉原則
LSP The Liskov Substitution Principle 里氏替換原則
ISP  The Interface Segregation Principle 接口分離原則
DIP The Dependency Inversion Principle 依賴倒置原則

 

一、單一職責原則(SRP)

從面向對象角度解釋這個原則為:”引起類變化的因素永遠不要多于一個。” 或者說 “一個類有且僅有一個職責”。這似乎不太好理解,特別是”引起類變化的因素永遠不要多于一個。”這句話更是有點虛,讓人有點摸不著頭腦。

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

看一個例子:

這是一個違反了“單一職責原則” 的類結構圖。
這里,Rectangle類做了下面兩件事:

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

并且,有兩個應用使用了Rectangle類:

  • 計算幾何應用程序(Computational Geometry Application)用這個類計算面積;
  • 圖形程序(Graphical Application)用這個類在界面上繪制矩形;

這違反了SRP(單一職責原則)。因為Rectangle類做了兩件事,在一個方法里它計算了面積,在另外一個方法了它返回一個表示矩形的GUI。這會帶來一些有趣的問題:在計算幾何應用程序中我們必須包含GUI。也就是在開發(fā)幾何應用時,我們必須引用GUI庫;圖形應用程序中Rectangle類的變化可能導致計算幾何應用程序的變化,編譯和測試,反之亦然。那么,怎么修改才能讓其符合單一職責原則呢?

 

答案是:拆分!拆分職責到兩個不同的類中,如:

  • Rectangle: 這個類應該只定義Area()方法;
  • RectangleUI: 這個類應繼承Rectangle類,并定義Draw()方法。

 

二、開放封閉原則 (OCP)

從面向對象設計角度看,這個原則可以這么理解:”軟件實體(類,模塊,函數(shù)等等)應當對擴展開放,對修改閉合。” 通俗來講,它意味著你(或者類的客戶)應當能在不修改一個類的前提下擴展這個類的行為。在OOD里,對擴展開放意味著類或模塊的行為能夠改變,在需求變化時我們能以新的,不同的方式讓模塊改變,或者在新的應用中滿足需求。

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

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

舉一個例子:

違反了開放封閉原則的類結構圖。

客戶端代碼直接面向服務器端的具體實現(xiàn)編程,缺乏靈活性。這樣如果服務器因為某些原因被其他服務器替換了,那么客戶端調用服務器的代碼也必須做相應的修改或替換。這其實就是”面向實現(xiàn)編程“的設計臭味!

那么,如何修改才能得到正確靈活的設計?

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

下面是正確的設計:

遵循開放封閉原則的類結構圖。

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

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

Liskov’s 替換原則意思是:”子類型必須能夠替換它們的基類型。”或者換個說法:”使用基類引用的地方必須能使用繼承類的對象而不必知道它。” 這個原則正是保證繼承能夠被正確使用的前提。通常我們都說,“優(yōu)先使用組合(委托)而不是繼承”或者說“只有在確定是 is-a 的關系時才能使用繼承”,因為繼承經常導致”緊耦合“的設計。
在基本的面向對象原則里,”繼承”通常是”is a”的關系。如果”Developer” 是一個”SoftwareProfessional”,那么”Developer”類應當繼承”SoftwareProfessional”類。在類設計中”Is a”關系非常重要,但它容易沖昏頭腦,導致使用錯誤的繼承造成錯誤設計。

看一個最最經典的例子:

遵循Liskov替換原則的類結構圖。

注:這里,KingFisher(翠鳥)類擴展了Bird基類,并繼承了Fly()方法,這沒有問題。

但是下面這個類結構圖就存在設計上的問題:

違反Liskov替換原則的類結構圖。

Ostrich(鴕鳥)是一種鳥,這毋庸置疑,并從Bird類繼承,這從概念上說沒有問題。但是鴕鳥它能飛嗎?不能,那么這個設計就違反了LSP。因為在使用Bird的地方不一定能用Ostrich代替。所以,即使在現(xiàn)實中看起來沒問題,在類設計中,Ostrich不應該從Bird類繼承,這里應該從Bird中分離一個不會飛的類NoFlyBrid,Ostrich應該繼承這個不會飛的鳥類NoFlyBrid。

 

為什么LSP如此重要?

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

四、接口分離原則(ISP)

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

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

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

下面這個例子充分的說明了”接口應該僅包含必要的方法,而不該包含其它的“。如果一個接口包含了過多的方法,應該通過分離接口將其拆分。

這是一個違反接口分離原則的胖接口。

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

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

 

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

五、依賴倒置原則(DIP)

這個原則的意思是:高層模塊不應該依賴底層模塊,兩者都應該依賴其抽象。其實又是”面向接口編程,不要面向實現(xiàn)編程“的內在要求。

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

你的汽車是由很多如引擎,車輪,空調和其它等部件組成,對嗎?

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

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

具體的車輪 EighteenInchWheelWithAlloy同樣屬于底層模塊,也依賴于抽象接口IEighteenInchWheel。

 

上面Car類有兩個屬性(引擎和車輪列表),它們都是抽象類型(接口)。引擎和車輪是可插拔的,因為汽車能接受任何實現(xiàn)了聲明接口的對象,并且Car類不需要做任何改動。

除SOLID原則外還有很多其它的面向對象原則。如:

 

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



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

相關熱詞搜索:solid