---
# System prepended metadata

title: '[2023 15th鐵人賽] Day10 - 透過圖文理解 S.O.L.I.D 原則'
tags: [SOLID, 2023鐵人賽, qiita]

---

[![hackmd-github-sync-badge](https://hackmd.io/8BvrDK1rSLG3QDlNVAbeXw/badge)](https://hackmd.io/8BvrDK1rSLG3QDlNVAbeXw)
###### tags: `2023鐵人賽` `qiita` `SOLID`
# [2023 15th鐵人賽] Day10 - 透過圖文理解 S.O.L.I.D 原則


![](https://imgur.com/yemjbew.png)
> 原文連結：[The S.O.L.I.D Principles in Pictures](https://medium.com/backticks-tildes/the-s-o-l-i-d-principles-in-pictures-b34ce2f1e898)
> 本文所刊登的圖片，皆屬原作者 Ugonna Thelma 所有。

這篇比較特別一點，是從「[イラストで理解するSOLID原則 - Qiita](https://qiita.com/baby-degu/items/d058a62f145235a0f007)」這篇翻譯得知英文原文，因此會以英翻中為主。

本文將以圖解方式，依序說明 SOLID 原則：
+ Single Responsibility 單一職責原則
+ Open-Closed 開放封閉原則
+ Liskov Substitution 里氏替換原則
+ Interface Segregation 介面分離原則
+ Dependency Inversion 依賴反向原則

以下正文開始。

---

[toc]


## 前言

若您熟悉[物件導向程式設計](https://en.wikipedia.org/wiki/Object-oriented_programming)，那麼您應該聽過 [**SOLID**](https://en.wikipedia.org/wiki/SOLID) 原則。

這五個軟體開發原則，是在建立軟體時應遵循的指南，使軟體更容易擴展和維護。這些原則是由軟體工程師 [Robert C. Martin](https://en.wikipedia.org/wiki/Robert_C._Martin) 所推廣。

網路上雖然有許多關於 **SOLID** 的優秀文章，但我很少看到有附圖片的範例。這對於像我這樣的視覺學習者來說，想要參與學習是有點困難的。

因此，本文的主要目的就是通過插圖來幫助理解這些原則，並強調每個原則的目標。

其中一些原則可能看起來很類似，但其實並不針對**相同的目標**。即使看起來相似，也可能滿足一個原則並違反另一個原則。

為了使本篇內容簡單易懂，將使用詞語「**Class（類別）**」，但請注意這個詞語在本文也適用於 **Function（函數）、Method（方法）或** **Module（模組）**。

> **更新***
> 
> 有留言在本文中提到，「開放封閉原則 OCP」可能違反了「單一職責原則 SRP」，但需注意本文目的是單獨解釋每一個原則。
> 
> 此外，責任（或角色）並不同於行動。在「單一職責原則」中，使用 "I am Painter"，而在「開放封閉原則」中，則使用 "I can Paint"。
> 
> 這一點非常重要，為了履行一個責任（或角色）可以執行多個動作。Class 應該具備一個責任（SRP），但實現該責任的功能應該能夠擴展的（OCP）。
> 

現在，讓我們開始吧！

## SOLID 原則

### S（Single Responsibility）單一職責原則

> 類別應該具備單一職責。
> 

![S](https://imgur.com/2B0SHkw.png)

如果一個類別有多個職責，將提高錯誤的可能性。因為修改其中一個職責，可能會連帶影響其他職責。

#### 目標

這個原則的目標是確保操作分離，即使變更可能導致錯誤發生，也不會對其他無關的操作產生影響。

### O（Open-Closed）開放封閉原則

> 類別應該對擴展開放，但對修改封閉。
> 

![S](https://imgur.com/psDnV2O.png)

修改一個類別的當前行為，將會對使用該類別的所有系統造成影響。

若希望類別執行更多功能，理想的方法是添加功能到既有的函數，而**不是**進行修改。

#### 目標

這個原則的目標是擴展類別功能，而不改變該類別現有行為。藉此避免在使用該類別的地方出現錯誤。

### L（Liskov Substitution）里氏替換原則

> 若 S 是 T 的子類別（subtype），則程式中 T 型的 Object 可被 S 型的 Object 替換，且不改變程式的任何屬性。

![L](https://imgur.com/M13Yu5F.png)

當一個**子**類別無法與其**父**類別執行相同的操作時，這可能會引發錯誤。

若由一個類別建立另一個類別，該類別會成為**父**，新的類別成為**子**。**子**類別必須能夠執行**父**類別能夠執行的所有操作。這個過程稱作**繼承**。

**子**類別在處理與**父**類別相同的請求時，將會產生相同的結果，或相同類型的結果。

圖片顯示**父**類別可提供咖啡（任何種類的咖啡），而**子**類別提供卡布奇諾是可接受的，因爲卡布奇諾屬於咖啡的一種，但提供水則是不可接受的。

當**子**類別不符合這些要求時，代表著**子**類別完全改變，如此即違反了這個原則。

#### 目標

這個原則的目標是強制一致性，確保父類別或其子類別能夠以相同的方式使用，而不會出現任何錯誤。

### I（Interface Segregation）介面分離原則

> 客戶端不應被強迫依賴於未使用的方法。

![I](https://imgur.com/60jZFbY.png)

在類別中執行不必要的操作將造成許多浪費，若該類別無法執行這些操作，則可能導致意外的錯誤。

類別應該只執行履行其職責所需的操作。其他操作應該完全刪除，或如果將來可能被其他類別使用，則應該移至其他地方。

#### 目標

這個原則的目標是將操作的集合分割成更小，以確保類別僅執行所需的功能。

### D（Dependency Inversion）依賴反向原則

> 高層模組不應該依賴於低層模組。兩者都應依賴於抽象化。
抽象化不應該依賴於細節。細節應該依賴於抽象化。

![D](https://imgur.com/jJG3QQb.png)

首先，簡單定義這裡使用到的術語。

**高層模組（或類別）**：使用工具執行動作的類別

**低層模組（或類別）**：執行動作所需的工具

**抽象**：表示連接兩個類別的介面

**細節**：工具的操作方式

在這個原則中，類別不應該與執行動作的工具混合在一起。相反地，工具應該與可以連接類別的介面融為一體。

此外，類別和介面都不應該知道工具的操作方式，但工具必須符合介面的規範。

#### 目標

這個原則的目標是透過引入一個介面，以減少高層類別對低層類別的依賴。

## 結語

本篇針對這五個原則進行說明，並強調其目標。這些原則的目標是降低發生問題的可能，使程式碼易於調整、擴展及測試。
非常感謝您的閱讀。希望您對這個主題有更深的瞭解，並且在閱讀本文的過程能像我寫作的過程一樣愉快。
若有任何問題或建議，歡迎留下評論。
