物件導向_SOLID五項鐵則_初探




今天要來分享設計模式中所探討的物件導向基本5項鐵則
基本上OOP的用意可以想
程式設計師不想寫重複程式碼(不需要理解細節)
直接重複呼叫調用就可

類以前我們說她是一個物件設計前的藍圖
裡面有靜態屬性  及 動態方法
實體化出來的為實質物件
以學生為例

類別藍圖
物件實體


Step1.開一個 堆(Heap)
Step2. 建立物件
Step3. 調用構造函數
Step4. 返回 Heap空間位址



類 和 類 之間是有關係的


1.關聯關係
一個類的方法參數是該類的物件

教授.teach(研究生A);


2.繼承關係


3.聚合關係(整個主體和部分)


4.實踐關係












基本上在程式撰寫的過程中通常

我們最常採納的  SOLID五項鐵則

S ---> Single Responsibility Principle(SRP)
O ---> Open/Close Principle(OCP)
L ---> Liskov Substitution Prnciple(LSP)
I  ---> Interface Segregation Principle(ISP)
D ---> Dependency Inversion Principle(DIP)



Single Responsibility Principle (SRP)
單一職責  設計模式



講白話一些就是  一個Class 只負責一件工作事項(單一職責)。


我們講一個小故事來做理解

通常在物件導向最常被舉例的不外乎是車子、狗等等諸如此類的非常明確之物件
====================================================================
假設你若要開一部車      那就需要汽車駕照
假設你若要開一台飛機  那就需要飛機駕照
假釋你若要開一艘快艇  那就需要快艇的駕照

你能想像給你一個變形金剛(多功能)可以變更為

一輛車、一艘船、一台飛機

可以路上行、水上遊、空中飛

等多功能


但是再想想

當他哪天故障之時 , 就可能需要會修飛機的維修師、會修汽車的維修師、會修船的維修師

來維修一起查看故障原因

當你想駕駛他時,不僅僅需要汽車駕照
還需要其他額外型態的駕照才可操控她




====================================================================
想想一家剛新開的小型家庭式餐廳
假若我提供的是日式兼義式料理甚至台式料理皆有
那可享而知
菜單肯定凌亂到不行
而且不僅需要顧用 日式廚師  甚至 台式及義式廚師皆要雇用
花費更多人力成本

此外若專Focus在某種特定飲食文化上
可以更精於該向區域的文化料理 , 也可節省人事成本
菜單更簡單凸顯出該餐廳所供料理的主要特色


====================================================================

一個class如果附加價值(功能)太多/工作過盛  就會像上述

變形金剛  和   多飲食文化餐廳

一樣容易在後續及使用上造成維護之困難


不過也不是說 單一職責

就是一個class就只有包含一種method功能方法


通常一輛車我可以定義他具備   左右轉  前進後退等等功能


一家餐廳的廚師  可以具備洗菜、洗米、烘烤、炒等等功能



而在設計上也不可過度設計

你必須明確去定義這個類別使用的目標人是誰為何物??

雖然汽車可以哥分出許多細碎零件
例如:方向盤、車頭燈、方向燈、引擎、油箱

但是對於一般行車人
他只要去知道如何左右轉、發動、前進後退即可
對於維修專員
才需要去知道細部零件

所以規劃Class之職責要視需求來訂定

單一職責帶來的好處:
(1)Class的複雜性降低
(2)可讀性提高
(3)可維護性提高
====================================================================
Open/Close Principle(OCP)

開放/封閉原則

---->模塊可擴展(充) , 但不可修改(對擴充開放,對更改封閉)

基本上這也是滿抽象的概念 ,

以往我們會說在寫程式終需要去保留後續增加額外功能的可擴充性

所以要求低耦合(Coupling)

何謂低耦合??
我們只針對部分區塊做修改,不牽一髮動全身!!!!!

想想今天你在用  水泥建立一棟別墅
你請建商幫你再多加出一面牆或一個樓層
結果他挖東牆補西牆  導致後續屋子地基不穩而倒塌

這就是一個非常具體的生活化例子

再看看一個生活化沒遵守開放/封閉原則(高耦合)
通常一般餐廳要煮蚵仔麵線或是宴會請客用的大鍋熬煮的愛玉或仙草茶等等
都會為了足夠分而加水導致整鍋味道不足
後來加了糖想補充味道而造成走味等等


登上合歡山通常我們不去特別改引擎或是車子其他核心部分
而是直接Focus在輪胎上  比方上雪鍊等等防護措施

在剛剛的水泥牆例子,基本上建商會採用根據目前地基
再多補充建材的方式(鋼筋鐵皮...等等)去做額外的擴充


這就是所謂的開放式擴充(以不影響主體為原則)
我們開放建商以目前地基去新增頂樓

我們開放讓車行老闆針對輪胎加上額外的雪鏈




====================================================================
Liskov Substitution Prnciple(LSP)

Liskov 替換原則


子類必須可替換它們的基類
你說這捨???  太抽象了.....
講白話一些, 一個系統中,通常子類是可以去替換掉其父親類 而不影響架構的。
只要是父類(Base Class)出現的地方子類(Sub class)就可以出現,並且還能替換成子類
也不會有任何錯誤異常發生。
通常會需要用這技巧是在於使用者(接手開發的人)根本不需要知道
是父類還是子類,反之則不可。


用程式碼來具體說明
定義一個槍的類
在此用抽象類,因為槍是個抽象的概念,大概
只知道要拿來射擊(具有射擊的功能)

而其餘可能分很多款的槍,各牌需要安置的子彈數、開啟方法、握法
、射擊方式都不同。

可能機關槍、步槍、來福...etc
各自射擊都不同

而對士兵(士兵物件)而言他們需知道的就是用槍擊殺敵人
在此setGun規範傳入參數要是Gun這個抽象的Base Class
於實際外部調用是給予子類(特定某牌的槍),也就是具體的實踐,而可提高程式碼共享性,
有需要修改、更擴充時候就可變輕鬆一點。




里氏替換好處
(1)代碼共享、減少實體化類的工作量
(2)提高代碼重用性、可擴充性

====================================================================
Interface Segregation Principle(ISP)

介面隔離原則

interface 功能盡量少:達到一個介面只服務一個子模組/商業邏輯。
模組之間的依賴,不應有用不到的功能可以被對方呼叫(不應該強迫實作沒用到的方法)。


是想像一下一台只有最基礎洗衣功能的洗衣機
跟一台洗加烘乾的二合一洗烘衣機
兩個我們都去給其實作二合一洗+烘的interface
會發生捨麼事情



可以發現只有單一功能的洗衣機根本不需要去實作烘乾
違背了 ISP 不應該強迫實作沒用到的方法

此時我們應該將肥大的interface做依稀功能切分乾淨的調整
至於需要多少功能就實作多少












====================================================================

Dependency Inversion Principle(DIP)

高層模組不該依賴低層模組(Class....)。
-->換言之,外部在調用的區塊(可能是你的main或多個程式檔案、其他的方法、函數當中)
不該頻繁修改。

兩者(高低層模組)皆應該依賴其抽象,抽象不該依賴細節,細節應該依賴抽象


在白話一點說,就是實例化的Class之間不發生依賴關係,其依賴關係
透過Interface或Abstract來產生。

沒遵守依賴倒轉原則的反面例子
來看一個反面的程式例子(剛開始常會這樣寫)
場景:司機和開的車
這裡在外部(高層模組中,在此是main,可能之後有多份其他檔案都有調用相關類)
實際應用時候
就可以發現由於Driver定義的drive方法規範要傳入的參數是一個具體的
Benz 物件,因此會導致彼此產生緊密依賴(耦合度高)。

當業務流程一需更改,比方要司機改開其他牌子的車
就要一而再,再而三地更動,低層的Class除了擴充新牌子車,你的Driver中方法還要一直改
傳入對應型別的車物件,不停更改 new 新的牌子的車,原先已經寫好並驗證好傳入的物件通通都要再修改。
可能在此你覺得我也只需要改main而已啊!!!只是實際場景可能是30~40個程式檔案中都有在呼叫這個Driver的方法。
頻繁地改會使系統穩定性容易變差~(可能會有漏改問題)


遵守依賴倒轉原則修改之後的例子

我們定義一段抽象關係的介面
任一具體Class只要實作該interface規範
都必須對應具體實踐該方法
車子介面中定義了run


driver介面則定義駕駛的方法

在實作中則傳入Interface抽象的型別,使外部可保有擴充彈性
更可避免Driver的class重複修改drive方法

最終外部高層模組調用
則都是依賴抽象介面來對應做具體對應物件的實體化,大大增加可擴充性。











Ref:
https://methodpoet.com/interface-segregation-principle/

https://www.thinkinmd.com/post/2020/03/13/oo-interface-segregation-principle/

留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header