天天看點

你真的了解 SOLID 面向對象設計原則嗎?

導讀:最樸實的語言給你講解 solid優質教程請關注微信公衆号“web項目聚集地”

縮寫

全稱

中文

s

the single responsibility principle

單一職責原則

o

the open closed principle

開放封閉原則

l

liskov substitution principle

裡氏替換原則

i

the interface segregation principle

接口分離原則

d

the dependency inversion principle

依賴倒置原則

一個類隻應承擔一種責任。換句話說,讓一個類隻做一件事。如果需要承擔更多的工作,那麼分解這個類。

舉例 訂單和賬單上都有流水号、業務時間等字段。如果隻用一個類表達,賦予其雙重職責,後果:

特有屬性和共有屬性互相摻雜,難以了解; 修改一個場景可能會影響另一個場景。

正确的做法是拆成兩個獨立的類。

實體應該對擴充是開放的,對修改是封閉的。即,可擴充(extension),不可修改(modification)。

舉例 一個商戶接入了多個付款方式,支付寶和微信支付,如果将調用支付api的類寫成:

那麼每次新加一種支付方式,或者修改原有的其中一種支付方式,都要修改payhandler這個類,可能會影響現有代碼。

比較好的做法是将不同的行為(支付方式)抽象,如下:

這樣,新增支付方式隻需要新增類,如果使用的是spring等容器,在xml配置對應key-value關系即可;修改已有的支付方式隻需要修改對應的類。最大化地避免了對已有實體的修改。

一個對象在其出現的任何地方,都可以用子類執行個體做替換,并且不會導緻程式的錯誤。換句話說,當子類可以在任意地方替換基類且軟體功能不受影響時,這種繼承關系的模組化才是合理的。

舉例 經典的例子: 正方形不是長方形的子類。原因是正方形多了一個屬性“長 == 寬”。這時,對正方形類設定不同的長和寬,計算面積的結果是最後設定那項的平方,而不是長*寬,進而發生了與長方形不一緻的行為。如果程式依賴了長方形的面積計算方式,并使用正方形替換了長方形,實際表現與預期不符。

擴充 不能用繼承關系(is-a),但可以用委派關系(has-a)表達。上例中,可以使用正方形類包裝一個長方形類。或者,将正方形和長方形作進一步抽象,使用共有的抽象類。

逸聞 “裡氏”指的是芭芭拉·利斯科夫(barbara liskov,1939年-),是美國第一個計算機科學女博士,圖靈獎、馮諾依曼獎得主,參與設計并實作了oop語言clu,而clu語言對現代主流語言c++/java/python/ruby/c#都有深遠影響。其項目中提煉出來的資料抽象思想,已成為軟體工程中最重要的精髓之一。(來源: 互動百科)

客戶(client)不應被強迫依賴它不使用的方法。即,一個類實作的接口中,包含了它不需要的方法。将接口拆分成更小和更具體的接口,有助于解耦,進而更容易重構、更改。

舉例 仍以商家接入移動支付api的場景舉例,支付寶支援收費和退費;微信接口隻支援收費。

第二種支付管道,根本沒有退款的功能,但是由于實作了paychannel,又不得不将refund()實作成了空方法。那麼,在調用中,這個方法是可以調用的,實際上什麼都沒有做!

改進 将paychannel拆成各包含一個方法的兩個接口payablechannel和refundablechannel。

高層次的子產品不應依賴低層次的子產品,他們都應該依賴于抽象。 抽象不應依賴于具體實作,具體實作應依賴抽象。 實際上,依賴倒置是實作開閉原則的方法。

舉例 開閉原則的場景仍然可以說明這個問題。以下換一種表現形式。

這種實作方式,payhandler的功能(高層次子產品)依賴了兩個支付processor(低層次子產品)的實作。

擴充:ioc和di 控制反轉(ioc)和依賴注入(di)是spring中最重要的核心概念之一,而兩者實際上是一體兩面的。

依賴注入 一個類依賴另一個類的功能,那麼就通過注入,如構造器、setter方法等,将這個類的執行個體引入。 側重于實作。 控制反轉 建立執行個體的控制權由一個執行個體的代碼剝離到ioc容器控制,如xml配置中。 側重于原理。 反轉了什麼:原先是由類本身去建立另一個類,控制反轉後變成了被動等待這個類的注入。 後記 網絡上很多文章中關于solid的介紹,語句都不通順,徒增了解難度。如果對基本釋義仍不能領會,可以參考 英文wiki。

繼續閱讀