今天柴咖啡店休,我們也休息一下,來聊一下觀念~~
哇,那既然 SOLID 五個原則講完了,例子我也看得懂,那以後我每次開發都強制使用,將五個原則貫徹始終,甚至把 SOLID 刺在手臂上,是不是可以從 junior 直接跳級成 CTO 了


想起我起初遇到一個很優秀的前輩跟我說的名言:「 軟體開發的根本是需求溝通 」
需求會變,就會需要成本,需要成本我們就要好好設計
我們來回顧一下 SOLID 的本質是什麼
「當需求變動時,怎麼設計程式碼,才能讓『變動的成本』不會隨著系統長大而指數爆炸?」
SOLID 的出發點是對的。但問題往往不是原則本身,而是套用的時機
系統還小的時候,什麼都塞在一起也跑得動。小黃(還記得嗎?最初寫系統的朋友)
一開始幫朋友寫的點餐系統,一個 class 搞定,沒人有疑問,但也可能是沒人看得懂程式碼
但系統長大之後,事情開始變得麻煩,阿柴接手後才發現改一個折扣邏輯,結果訂單顯示壞了;
加一個新的付款方式,卻動到不相關的庫存計算;
每次改東西都要祈禱不要壞其他地方
SOLID 就是從這些痛苦裡歸納出來的原則,前人在被系統折磨過(?)之後,整理出來的一套「怎麼讓改動的代價不那麼大」的經驗法則
什麼時候該用?
單一職責原則(SRP):這是唯一在開發當下就應盡量遵守的原則。讓一個函數或類別只專注做一件事,幾乎沒有任何抽象成本,且能立刻提升可讀性與測試性
核心業務邊界明確時:如果你在開發初期就已經 100% 確定某些部分未來必然會替換(例如資料庫存取層、第三方金流 API、寄信服務),直接使用依賴反轉(DIP)引入介面也是合理的
什麼時候該等出現「痛」時再重構引入?
開放封閉原則(OCP):出現第 3 次變動(符合 Rule of Three:事不過三),代表業務邏輯有擴充需求,此時才透過多型或策略模式重構
介面隔離原則(ISP):先寫出簡單好用的類別;當發現某些客戶端被迫實作一堆用不到的空方法時,再來拆分介面
里氏替換原則(LSP):當子類別開始出現「覆寫父類別方法卻丟出 NotImplementedException」等破壞契約的行為時,代表繼承關係設計錯誤,應重構改用組合(Composition)
阿柴是在需求開始變複雜、改東西開始會出錯的時候,才學會一條一條把這些原則用進去
既然定義好 SOLID 的使用時機,那我們延伸探討三個觀念,KISS、YAGNI、DRY
這三個老大哥都比 SOLID 資歷更老,也更直觀:
KISS(Keep It Simple, Stupid)
源自美國海軍 1960 年代的工程設計原則,後來被軟體業借用:能簡單解決的問題,就不要故意做複雜
不必要的複雜度本身就是 bug 溫床,多一層抽象,就可能多一個出錯、也多一個要維護的地方
kaisheng 大大解釋得很清楚也很棒
任何傻子都能寫出電腦看得懂的程式,而優秀的程式設計師則會寫出讓人類看得懂的程式
KISS 主要圍繞在可讀性的部分,畢竟人一天的認知是有限的,如果用很複雜的方式去解決簡單的問題,不僅看的人很辛苦,維護的人也很辛苦
相反,如果在開發過程實踐 KISS 原則,可讀性提高,保持簡單和直觀,避免過度設計與抽象化,也會讓維護的成本降低
那什麼時候該提醒自己需要 KISS?
當發現自己在寫一段「目前沒有任何需求」的彈性寫法,或單純想讓程式碼看起來更專業而硬加一層設計時,就是該喊卡的時候
YAGNI(You Aren't Gonna Need It)
出自 Extreme Programming(極限編程),由 Ron Jeffries 提出:不要為了「將來可能用得到」而現在就先做
我的理解是,開發應專注於當下該解決的需求,而不是針對尚未發生的事情,去多設計彈性或者不必要的功能
畢竟假設多包的那層彈性,十次有九次,等真的需求來的時候,會發現當初猜錯方向,起初的自我感動設計彈性反而會變成先拆掉的包袱
任何變動都會有成本,而如何讓越低的成本敲動最大的效益,我想是一個優秀的工程師的必備條件
DRY(Don't Repeat Yourself)
最後一個,出自《The Pragmatic Programmer》,Andy Hunt 與 Dave Thomas 提出,原文是:
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
起初看到這段時,我在想,代表出現兩次以上的程式碼,通通都要濃縮嗎? 這裡我們先來看看以下的例子
class Coupon { public decimal Rate = 0.1m; }
class Tax { public decimal Rate = 0.1m; }
兩個 Rate 都是 0.1,那依照 DRY 的方式,將欄位抽成共用的基底類別:
class Percentage { public decimal Rate = 0.1m; }
class Coupon : Percentage { }
class Tax : Percentage { }
好像合理,但我們區分這兩個知識體來說, Coupon.Rate 是「折扣打幾折」,Tax.Rate 是「稅率抽幾趴」,兩件事本來就無關,只是剛好都設計成 0.1,並不屬於同一份知識
所以 DRY 講的是在開發上不應該知識、意圖上的重複,強調的是「knowledge」而不是「code」
KISS、YAGNI、DRY 加上 SOLID,各自講起來都成理,但實際改程式碼的時候,總不可能五六條原則同時在腦中打架,想完腦袋也下課了
所以我把這幾個原則,初步想了一套篩選機制
先確認需求是否存在,再確認是否複雜化,再確認知識是否重複,最後再依邏輯套用 SOLID 去處理
但人跟專案都是複雜的,這系列主要還是培養自己的判斷力,而不是學會一個功夫就要打遍天下無敵手
明天柴咖啡店營業,我們也要正式進入創建型 Pattern 的第一站:Factory Method
如果看不懂我寫的,可以看看延伸的資料,希望可以幫助到你
軟體設計原則 KISS (Keep it simple, stupid)