引子:考研失敗後的反思#

小菜剛得知考研成績差兩分未過分數線,因為前段時間全力複習而沒準備求職,現在班上同學都簽了大公司,自己卻一無所有。

大鳥用「鄧小平的一國兩制」做比喻:

  • 大陸的社會主義制度不能修改
  • 香港、澳門長期在資本主義制度下管理
  • 強行修改香港制度不合理 → 加一種制度(一國兩制)

這正是軟體設計裡的開放-封閉原則(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]

  • 開發人員應該僅對程式中呈現出頻繁變化的那些部分做出抽象
  • 過早抽象會把本該簡單的設計做得非常複雜,得不償失
  • 查明可能發生的變化等待越久,創建正確抽象越困難 → 開發工作展開不久就應留意可能的變化

故事的啟示#

考研是追求,學習計劃雷打不動——這是「對修改關閉」。

但完全放棄看招聘訊息、寫履歷,等於放棄了眾多機會——抽出一點時間應對求職並不影響複習,這是「對擴展開放」。

全力以赴是必須,兩手準備也是靈活處事的表現。