引子:考研失敗後的反思#
小菜剛得知考研成績差兩分未過分數線,因為前段時間全力複習而沒準備求職,現在班上同學都簽了大公司,自己卻一無所有。
大鳥用「鄧小平的一國兩制」做比喻:
- 大陸的社會主義制度不能修改
- 香港、澳門長期在資本主義制度下管理
- 強行修改香港制度不合理 → 加一種制度(一國兩制)
這正是軟體設計裡的開放-封閉原則(The Open-Closed Principle, OCP)。
開放-封閉原則(OCP)#
開放-封閉原則:軟體實體(類別、模組、函式等等)應該可以擴展,但是不可修改。[ASD]
兩個特徵:
- 對於擴展是開放的(Open for extension)
- 對於更改是封閉的(Closed for modification)
我們在做任何系統時,都不要指望需求一旦確定就不再變化——這是不現實也不科學的想法。
既然需求一定會變化,那麼好的設計應該讓系統面對需求的變化時相對容易修改,而不是新需求一來就要把整個程式推倒重來。
不能完全封閉#
絕對的「對修改關閉」是不可能的。無論模組多麼封閉,都會有無法封閉的變化。
設計人員必須先猜測最有可能發生的變化,然後構造抽象來隔離那些變化。[ASD]
那如果猜錯了呢?
- 預先猜測很難精準
- 但可以等到變化發生時立即採取行動[ASD]
- 同一地方第一次摔跤不是你的錯,再次在此摔跤就是你的不對了
應對變化的時機#
最初編寫程式時,假設變化不會發生。當變化發生時,創建抽象來隔離以後發生的同類變化。[ASD]
以第 1 章的計算器為例:
- 初版只有加法 → 寫在 client 類別中
- 加上減法 → 發現需要改原本類別,違反 OCP → 重構:引入抽象
Operation類別,用繼承與多型隔離 - 再加乘除法 → 不需修改 client、加法、減法,只需新增乘法、除法子類
- 面對需求,透過增加新程式碼來改動,而不是修改既有程式碼[ASD]
classDiagram
class Client
class Operation {
+double NumberA
+double NumberB
+GetResult() double
}
class OperationAdd
class OperationSub
class OperationMul
class OperationDiv
Client ..> Operation
Operation <|-- OperationAdd
Operation <|-- OperationSub
Operation <|-- OperationMul
Operation <|-- OperationDiv何時該抽象?#
開放-封閉原則是物件導向設計的核心,遵循它能帶來可維護、可擴展、可複用、靈活性好的好處。
但對應用程式的每個部分都刻意抽象,同樣不是好主意。
拒絕不成熟的抽象和抽象本身一樣重要。[ASD]
- 開發人員應該僅對程式中呈現出頻繁變化的那些部分做出抽象
- 過早抽象會把本該簡單的設計做得非常複雜,得不償失
- 查明可能發生的變化等待越久,創建正確抽象越困難 → 開發工作展開不久就應留意可能的變化
故事的啟示#
考研是追求,學習計劃雷打不動——這是「對修改關閉」。
但完全放棄看招聘訊息、寫履歷,等於放棄了眾多機會——抽出一點時間應對求職並不影響複習,這是「對擴展開放」。
全力以赴是必須,兩手準備也是靈活處事的表現。