關於 Design Principle 本篇將討論以下幾個問題
1. 關於 Others
2. Keep it simple, stupid
3. You aren’t gonna need it
4. Don't Repeat Yourself
5. Law of Demeter / The Principle of Least Knowledge
1. 關於 Others
避免誤會先說明一下,Others 並不是什麼新的原則,而是指 SOLID 以外的常見原則,本篇將會介紹以下四種原則
縮寫 | 名稱 | 中文 |
---|---|---|
KISS | Keep it simple, stupid | (略) |
YAGNI | You aren't gonna need it | (略) |
DRY | Don't Repeat Yourself | (略) |
LoD | Law of Demeter The Principle of Least Knowledge |
迪米特法則 最小知識原則 |
2. Keep it simple, stupid
保持簡單, 笨拙
簡單是個很抽象的概念,什麼樣的程式才算是簡單?
舉例來說,使用 Autofac 將服務註冊進 ContainerBuilder 的時候,使用 Reflection 取得 Assembly 內所有 Service & Component 來註冊,省去一一列出 Service & Component 的麻煩,之後新增 Service & Component 都不必修改 ContainerBuilder。
上面說到使用 Reflection 實作這段程式本身不算簡單,但使用上卻很簡單很方便,甚至什麼都沒做就會自動將 Service & Component 加到 ContainerBuilder 中了,一直到這邊都沒什麼問題,不過其實 Autofac 有一個RegisterAssemblyTypes()
方法,提供 Assembly Scanning 的功能,比起自己用 Reflection 來說,使用 Autofac 提供的方法更加簡單,也更好維護。
關於保持簡單有以下幾點可以參考
- 不要為了單純縮短程式碼而使用平常少見到的寫法(e.g. 位元與移位運算子)
- 不要重複造輪子(e.g. 如上述例子中已經有方法可以呼叫就不要自行實作)
- 不要為了不知道什麼時候才會擴充的功能預留擴展點(e.g. 硬要套用 Design Pattern 造成過度設計)
3. You aren't gonna need it
你不會需要它
可以把 YAGNI 看作是 KISS 的子集,YAGNI 主要是專注在要不要做上,而關於 YAGNI 有以下幾點可以參考
- 不要實作不知道什麼時候才會用到的功能
- 不要預留不知道什麼時候才會用到的擴展點
真的有需求再來花時間重構也不遲,除了不用為了當初預留擴展點的架構煩惱之外,還更符合實際需求。
4. Don't Repeat Yourself
不要重複
常見的重複有以下幾種
- 程式碼重複
- 功能重複
- 執行重複
程式碼重複最主要的原因應該是開法者必備的複製貼上大法了,遇到相同需求時應該避免複製貼上,而是將相同的部分抽離出來改為共用方法。
功能重複但實作細節不同(e.g. 相同的參數 & 回傳值),這部分通常是開發者間缺少溝通造成重複造輪子的狀況,除了加強溝通外,發現時也應該將重複的功能排除,只保留一份共用。
執行重複常見於取資料(e.g. 先到資料庫取得UserName
,之後又再取UserId
,但其實是同一筆資料)跟驗證時(e.g. 呼叫的每一層都重複的驗證 is null),這部分也許開發過程中沒注意到,但在 commit 之前可以再次順過程式邏輯,確認是否有執行重複的問題。
5. Law of Demeter / The Principle of Least Knowledge
迪米特法則 / 最小知識原則
Law of Demeter / The Principle of Least Knowledge 提倡
- 每個單元對其他單元的知識應該有限,只與當前單元密切關連
- 每個單元只與朋友交談,不和陌生人說話
- 只與直系朋友交談
舉例來說,有一個定時處理資料的排程,每次執行排程就會寫開始執行時間 & 結束時間的 log 到資料庫。
理想情況是透過_log
將時間寫入資料庫,而處理資料排程()
不會知道存入資料庫的邏輯,完全交由_log
來處理
public void 處理資料排程()
{
_log.Info(DateTime.Now, StatusType.Start);
// 處理資料實作
_log.Info(DateTime.Now, StatusType.End);
}
但若是今天我們透過_log
取得 Context 並由處理資料排程()
自己 Insert 資料,則是一次將 LoD 的三個規則都打破了,甚至還違反了 DRY 的執行重複,重複呼叫_log
取得 Context
public void 處理資料排程()
{
_log.GetContext.Insert(new Log { Time = DateTime.Now, Status = StatusType.Start });
// 處理資料實作
_log.GetContext.Insert(new Log { Time = DateTime.Now, Status = StatusType.End });
}