物件導向 個人心得(2
不知自己不知道, 那你會以為你知道.
在 2018 年左右建立的觀念(參考此篇
前言
主要記錄從 2018~2021 三年來關於物件導向的觀念上是否有什麼改變, 所以想在補充一點資訊後產生此篇.
此篇參考的資訊:
程式如何正確撰寫 ? | 物件導向程式設計 - SOLID 設計原則 : SRP、OCP、LSP、ISP、DIP
Laravel、Ruby on Rails、Django … 使用 Active Record Pattern 框架的最大問題:到底該把 save 寫在哪裡?
OOP 程式開發:用一個簡單的數學公式來幫忙設計類別
元件如何正確使用 ? | 元件耦合性三大原則 : ADP、SDP、SAP
運算思維的核心 - 抽象化
「用一個簡單的數學公式來幫忙設計類別」
- 解決 God Object 上帝物件
- 把相關的 property 跟 method 抽出來, 很輕易就能拆出多個 CR 更高的類別.
- 當你這麼做的同時通常很神奇地,類別命名會自然浮現.
- Long Method 過長的解決方法, 可分兩步驟改寫
(前提: 只在職責較為清晰的類別用以下步驟, 因職責不明確的類別通常是 God Object 若用以下步驟抽為屬性時, 通常會難以辨識屬性的定位, 更多的是模糊關注點.)
- 步驟一
- 先把每段任務拆成各自的多個小 method, 把用到的變數傳進去
- 你會發現同樣的好幾組變數, 反覆傳到好幾個 method, 似乎變得更糟糕了
- 步驟二
- 這些反覆傳送的參數, 變成類別的 property
- 你會發現很多 method 不再依靠參數, 而是轉跟 property 互動
- 因這些 property 反覆在多個 method 被使用, 這時 CR 也跟著提高了
- 內聚係數 (Cohesion Ratio) CR 越高越好:
(每個方法用到的屬性數量加總)/(屬性的數量 * 方法的數量)
SOLID 設計原則 : SRP、OCP、LSP、ISP、DIP
- SRP
- 一個模組應該只對唯一的一個角色負責
- 單一職責實際上是一種「分類」的方法,依據的是「不同角色的使用者」、「以不同視角的觀察」。
- OCP
- 一樓已經完成, 若需追加新的設計只能從二樓開始
- 不應再為了某種需求在一樓的牆壁鑽孔、打洞
- 萬一剛好是某個重要結構,可能導致傾斜或倒塌,這樣肯定得不償失
- LSP(要有繼承關係)
- 若對於型態 S 每個物件 o1 都存在一個型態為 T 的物件 o2
- 使得在所有針對 T 編寫的程式 P 中, 用 o1 替代 o2 後, 程式 P 的行為功能不變, 則 S 是 T 的子型態
- 上述簡而化之就是,子類別能替代父類別。如果發現子類時常覆寫父類方法,或是父類的方法改成可覆寫。就要注意子類別是否分類錯誤,或者父類別定義模糊 …等因素導致。
- ISP
- DIP
- 高層次的模組不應該依賴於低層次的模組, 兩者都應該依賴於抽象介面
還有「元件的內聚性原則」與「元件間的耦合性原則」
-
SDP 穩定依賴原則 (stable dependences principle)
# I 介於 0 ~ 1: 0 最穩定, 1 最不穩定
# FanIn 輸入依賴度: 外部有多少類別會依賴本身元件中的類別
# FanOut 輸出依賴度: 本身元件中有多少類別會依賴外部的類別
-
SAP 穩定抽象原則(stable abstracions principle)
- 「抽象」: 則代表的是 只表達「概念」但並不「實作」
- 重要的元件, 應該越只表達純粹的「標的(一種概念可已是 需求、目標、行為、分類/歸類)」
- 核心原則即「隱藏細節」與「抽出標的(可已是 需求、目標、行為)。舉個例子:
- 設計排程任務,每個排程任務的流程與細節都不同。如果針對「流程」與「細節」去抽象化,那這個抽象只能局限於某個排程使用。
- 進一步去思考會想到「彈性」、「擴充」、「可讀性」
- 那對於排程任務這個情境,可以抽出行為的特徵比如像是「初始設定」、「執行任務」… 這類標的。
- 抽出上述標的後可讓抽象變得較為彈性,以達到彈性與多型的需求。
- 抽象化沒有絕對的對與錯,只有較佳或較適的可行方案。
- 抽象是設計核心元件的重要指標
- 抽象是「標的」, 它沒有一個特定的表徵. 舉個例子 =>
EX => 餐廳與廚師
# 由廚師本身會煮什麼菜, 來決定餐廳會有什麼樣的菜單. 但對於一定規模的餐聽, 可能不是一個好的情況
# 餐廳改成用 菜單 的概念來決定廚師要做哪些事情
# 就是將表達「概念」的「菜單」, 當成是最穩定的核心元件
-
一般程式架構以常見的依賴關係由核心往外層可分為:infrastructure => domain layer (商業邏輯) => Application layer => User Interface
- domain layer
- 通常就是系統的核心, 所以 Domain 的 Service 通常會宣告成 Interface 的介面
- 創建新的類別實作(implements) 該 Service 的介面
- 不應直接做資料存取, 比如調用 DAO(指 repository) 來完成資料保存的任務 <= 新觀念
- 借鑒了「SAP 穩定抽象原則」, 越穩定的元件應該越抽象
- Application layer
- 負責流程作業, 相比於 Domain 變動性是比較大的, 雖然同樣有 Service 的類別, 但不創建 Interface 的介面
- 應該調用了 Domain layer 後, 再調用 DAO <= 新觀念
- 借鑒了「SDP 穩定依賴原則」把容易變動的東西, 變得更加容易改動
- Infrastructure => 通常放共用工具的地方
SDP & SAP 兩個原則總結:
#「穩定」與「不穩定」並不是一個「好」與「壞」的關係
# 最容易變動的「User Interface 使用者介面層」、「Application layer 應用層」, 就是放在上兩層, 向下依賴
# 越核心的通常要越少依賴, 且越抽象. !!主要明確行為與特徵. 在測試上也方便抽換!!
MVc 在各大軟體開發商推出的 frameworek, 會讓新手誤以為就是這樣 (觀念修正與思想衝擊)
文章:
MVC是一個巨大誤會
框架不應該有「MODELS」資料夾
整理兩篇文章的要點:
- 現行 WEB 主流的 MVC 框架, 其實架構大多為 MODEL2
- 雖不是 MVC 但 MODLE2 的學習成本很低, 符合商業模式
- 不是小白的軟體工程師, 應該準備開始煩惱軟體架構這件事
反思:
- 當用了現行別人設計好的框架時, 不小心會掉入舒適圈. (糖果有時是毒藥)
- 想當年紅遍全台的 WEB FORM, 為什麼新專案已不見蹤影. (經歷過的就知道再說什麼)
- 多想想與試著了解: 為什麼會誕生、簡單的概念、怎麼達成的、簡單的原理…