English version:
英文版在 hackmd
本文包涵:
- 物件導向的 SOLID 設計原則
- 常見系統設計模式
物件導向的 SOLID 設計原則
以物件導向 (Object-oriented programming, OOP)設計程式時,常遵循 SOLID 原則:
- S: 單一職責原則 (Single responsibility principle):一模組一功能
每個模組 (module)或類別 (class)只做一件事,利於維護與測試
例如:處理登入模組,不該出現結帳功能 (白話:菜刀不兼鍋鏟職)
- O: 開放封閉原則 (Open-closed principle):新需求新模組
程式碼應對「擴展」開放,對「修改」封閉
例如:想增加登入驗證,應「另建模組」,而非「直些修改舊模組」
- L: 里氏替換原則 (Liskov substitution principle):子類別替代父類別,程式功能應不變
後代需具備祖先的所有特徵
例如:卡車屬於車,但車不一定是卡車 (卡車有輪胎、引擎等一切車子應有的組件,但「車」不只有卡車一種)
若你想開卡車去旅行,它當然能勝任 (旅遊品質是另一回事)

敞篷車、轎車、卡車都能稱作車,所以都能代步 (功能不變)
圖源:移幣,i.e. 我
- I: 介面隔離原則 (Interface segregation principle):不相關的功能就丟掉
不應強迫用戶實作不會用到的功能
例如:我只想 掃描
文件,使用 印表機
介面時,就不該強迫我使用 列印
功能
- D: 依賴反轉原則 (Dependency inversion principle):改動低階模組不應影響高階者
高階模組不依賴低階模組,而應依賴抽象 (Abstraction),低階模組實作抽象
例如:電視遙控器 (低階)壞了,只需要再買一個,不用連電視 (高階)都換掉
因為電視不依賴遙控器,兩者依賴特定訊號協定 (抽象)
SOLID 只是指導原則,設計仍保有高度彈性
有基本概念後,就來看看常見的設計模式吧!
常見設計模式
先看統整再細說:

常見設計模式總覽
製表人:我自己
原型 (Prototype pattern)
先做出樣品,再靠複製來生產物件 (樣品 = 原型 = 模板)
- 好處是:
- 效率高
做過報告就知道,複製貼上比從頭打字快多了
- 容易管理
想更新產品,只需修改模板
- 範例:
Ctrl
+C
→Ctrl
+V
當然不會這麼爛XD
- 「用履歷模板」就是個常見案例,因為「複製」了模板,並對其進行修改

原型 (Prototype):套模生產
圖源:又是我
單例 (Singleton)
一類別 (class)一實例 (instance),並提供全域存取點
- 優點:
- 資源共享
全域存取點共用,即同個資源池大家分
- 實例化效率高
實例化 (instantiation)時,物件才取得資源。所以若只需實例化一次,就不需要不斷獲取資源 (想像不斷開關冷氣 vs 開著冷氣,前者更耗電)
- 管理方便
所有人 (或物件)都需從全域存取點獲得服務,則只需管理該存取點,不必為每個終端 (使用者/物件)個別服務
- 範例:
- 資料庫連線池 (connection pool)
這是個經典案例,若每個用戶訪問時,都需取得資料庫連線、用完再關閉,會很耗資源
反之,先取得一堆連線 (i.e. 連線「池」),使用者訪問時分配一個給他,用完再丟回池中,後者較省資源
- 日誌管理系統 (Log system)
電腦只需一個日誌管理員,處理各種系統日誌
- 設定檔管理員
各程式可有獨立設定檔,但讀取可用同個管理員
- 政府機關
這應該最好理解。不會因為你我都要辦護照,政府就生出兩個外交部。
「政府」即為類別 (class),「外交部實體」便是實例 (instance)
- 然而,單例有些缺點:
- 違背 SOLID 的「單一職責原則 (Single responsibility principle)」
- 高耦合性 (所有東西都掛在一起)
→ 不利單元測試 (unit test)
→ 修改局部程式碼,可能影響其他功能 (牽一髮動全身)
- 全域變數污染 (global variable corruption)
全域存取點即為全域變數
- Multi-Thread(多執行緒,亦稱多線程) 問題
若程式沒特別處理,每個執行緒都會產生實例
→ 可藉雙重檢查鎖定 (Double-Checked Locking Pattern,又稱「鎖暗示 (lock hint)」)解決 --- 非本文重點,故不在此贅述

單例模式 (Singleton):一類別一實例,開給全域存取
圖源:還是我
工廠模式 (Factory Pattern)
製程與使用分離,i.e. 終端使用者毋需瞭解生產過程,出張嘴下單就好
- 工廠的功能便是製造產品,此「產品」即為物件 (object)
- 靠抽象 (abstraction)隔離製程
- 使用者只需實例化物件,至於物件怎麼產出的,不必在乎 (通常也無法得知)
- 優點:
- 高靈活性
生產新品時,不必修改上下游程式碼
- 易維護
工廠程式碼改不到,所以有問題一定是你的問題
- 乾淨程式碼 (Code clarity)
- 範例:
- 做披薩
還記得上次訂披薩的流程嗎?看菜單 → 下單 → 取貨 → 享用
裡面可有個步驟,需要知道披薩製作過程?
除非你是廚師,否則一般人吃 100 次披薩,也不會掌握製作細節
- 披薩餐廳就是工廠
- 客人是使用者
- 披薩是物件
- 汽車工廠
同理,買車時僅需選輛買得起、好看、實用的車,我哪管車怎麼蹦出來的?
- 工廠,嗯,就是工廠
- 買家是使用者
- 汽車是物件
抽象工廠 (Abstract Factory Pattern)
造工廠的藍圖,亦即工廠的工廠
- 製作相關工廠前,不須指定具體類別 (class)
- 關係:抽象工廠
- 工廠
- 物件
- 範例:
- 圖形化介面設計藍圖 → 抽象工廠
- 給 Windows 的圖形化介面 → 工廠
- 介面上的按鈕 → 物件
- 給 Mac 的圖形化介面 → 工廠
- 介面上的按鈕 → 物件
製造者模式 (Builder Pattern)
製程各步驟拆開,由不同製造者 (builder)負責
- 作法:一步驟一製造者,製造者僅負責份內工作
- 好處:
- 解決複雜問題
大工程拆成小任務,自然容易許多
- 避免跨度建構函式 (telescoping constructors)
有種情況是,一個類別 (class)為了接不同數量的參數 (parameter),而建立多個建構函式 (constructors),屬於一種反面模式 (anti-pattern),設計上應避免

製造者模式 (Builder pattern):拆分製造程序,各步驟由一個製造者負責
圖源:仍是我How old am I? 怎麼老是我
依賴性注入 (Dependency Injection Pattern)
模組 (module)或類別 (class)應外包「所需」功能,而非自產自銷
- 名詞解釋:
- 依賴性 (Dependency):做某件事所需的東西
車靠引擎跑 → 車
依賴引擎
- 注入 (Injection):從外部給予某物
為手機「裝」電池,而非把電池做在手機上,如同現代無良手機品牌們
→ 將電池注入手機
- 優點:
- 低耦合度 (loose coupling)
- 高靈活性 (Flexibility)
- 容易測試
- 乾淨程式碼 (clean code)

依賴性注入 (Dependency Injection Pattern):外包功能,而非自產自銷
圖源:當然,只會是我
最後,來看看軟體服務常用的設計形式
軟體服務設計模式
三層式架構 (3-tier architecture)
服務拆三層:使用者 - 伺服器 - 資料庫

3層式架構 (3-tier architercture):用戶端 - 伺服器端 - 資料庫端
圖源:不是我還會是誰?
- 哪三層?
- 展示層 (presentation tier):顯示給使用者的門面 (前端)
- 商業邏輯層 (business logic tier):處理核心要務 (伺服器後端)
- 資料存取層 (Data access tier):資料取用、存儲 (資料庫)
- 好處?
- 關注點分離 (Separation of Concerns) → 更好維護
- 高可擴展性 (Scalability)
- 安全性高
- 可重用性
- 解耦 (Decoupling)
既然已拆成三層,就能依此結構延伸出:
- MVC
- MVP
- MVVM
MVC
模型-視圖-控制器 (Model-View-Controller)
- 模型 (Model):商業邏輯處理
- 視圖 (View):用戶顯示模組
- 控制器 (Controller):業務分派
MVP
模型-視圖-展示器 (Model-View-Presenter)
- 模型 (Model):商業邏輯處理
- 視圖 (View):用戶顯示模組
- 展示器 (Presenter):處理資料並送至視圖
MVVM
模型-視圖-視圖模型 (Model-View-ViewModel)
這東西還真拗口
- 模型 (Model):商業邏輯處理
- 視圖 (View):用戶顯示模組
- 視圖模型 (ViewModel):處理資料,並轉成視圖用格式
- 繫結模型與視圖
- 在此改資料可影響視圖
可獨立做單元測試 (unit test),GUI 介面非必要
有點混亂了嗎?
想像你進入咖啡廳,看著菜單選品項:菜單就是視圖 (view)
- 如果用平板點餐,平板的程式交辦煮咖啡、送餐,則:平板便是控制器 (controller)
- 點餐後,平板預先顯示咖啡完成畫面:平板是展示器 (Presenter)
- 若是服務生來點餐,點完再去操作咖啡機:服務生即為視圖模型 (ViewModel)
最後來個統整:

MVC、MVP、MVVM 統整
製表人:我自己
內容如有錯誤,歡迎交流指教
更瞭解系統設計了吧?
感謝您浪費時間撥冗閱讀,咱們下篇見!