# 26. Design Principles - Others ###### tags: `DesignPatterns` ## 關於 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<br>The Principle of Least Knowledge | 迪米特法則<br>最小知識原則 | --- ## 2. Keep it simple, stupid #### 保持簡單, 笨拙 簡單是個很抽象的概念,什麼樣的程式才算是簡單? 舉例來說,使用 Autofac 將服務註冊進 ContainerBuilder 的時候,使用 Reflection 取得 Assembly 內所有 Service & Component 來註冊,省去一一列出 Service & Component 的麻煩,之後新增 Service & Component 都不必修改 ContainerBuilder。 上面說到使用 [Reflection](https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/reflection) 實作這段程式本身不算簡單,但使用上卻很簡單很方便,甚至什麼都沒做就會自動將 Service & Component 加到 ContainerBuilder 中了,一直到這邊都沒什麼問題,不過其實 Autofac 有一個`RegisterAssemblyTypes()`方法,提供 [Assembly Scanning](https://autofaccn.readthedocs.io/en/latest/register/scanning.html) 的功能,比起自己用 Reflection 來說,使用 Autofac 提供的方法更加簡單,也更好維護。 關於保持簡單有以下幾點可以參考 - 不要為了單純縮短程式碼而使用平常少見到的寫法(e.g. [位元與移位運算子](https://docs.microsoft.com/zh-tw/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators)) - 不要重複造輪子(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`來處理 ```C# public void 處理資料排程() { _log.Info(DateTime.Now, StatusType.Start); // 處理資料實作 _log.Info(DateTime.Now, StatusType.End); } ``` 但若是今天我們透過`_log`取得 Context 並由`處理資料排程()`自己 Insert 資料,則是一次將 LoD 的三個規則都打破了,甚至還違反了 DRY 的執行重複,重複呼叫`_log`取得 Context ```C# public void 處理資料排程() { _log.GetContext.Insert(new Log { Time = DateTime.Now, Status = StatusType.Start }); // 處理資料實作 _log.GetContext.Insert(new Log { Time = DateTime.Now, Status = StatusType.End }); } ``` --- ## 總結 ### 要讓現有的程式更符合 Design Principles 就脫離不了「重構」,而重構範圍可大可小,大到整個專案架構調整,小到兩個 class 之間解耦,都可以算是重構,且持續重構能讓專案保持擴充彈性與維護性,不過重構若是沒有充足的單元測試與重構者扎實的程式基礎為依靠,那就真的是在製造工作機會了。 --- ## 參考資料 1. [Wikipedia KISS](https://ppt.cc/fAYF8x) 2. [Wikipedia YAGNI](https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it) 3. [Wikipedia DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) 4. [Wikipedia LoD](https://en.wikipedia.org/wiki/Law_of_Demeter) --- ## 新手上路,若有錯誤還請告知,謝謝