2020年4月29日 星期三

The Clean Code Talks - Don't Look For Things! 整理

重點整理:
  • 用細緻的方式回到基本去討論依賴注入(Dependency Injection)
  • 測試系統時需要實例化物件,實例化物件時常放了很多測試替身(Test-double)進去,讓測試更困難
  • 類別的靜態變數有時牽涉到單例(Singleton)設計模式,要一起實例化,讓測試更麻煩
  • 實例化很困難意味著難以測試
  • 超文本文件類別為例,文件只在乎客戶端產生的文件,不需要瞭解客戶端如何產生文件
  • 車類別為例,車只在乎產生的引擎,不需要了解引擎怎麼產生
服務定位器模式(Service Locator pattern)的問題:
  • 違反得墨忒耳定律(Law of Demeter a.k.a. LoD,簡單說即只拿直接需要的物件)
  • 將服務定位器傳給要實例化的物件,看建構子參數不夠,還得追建構子程式碼才知道相依的物件,這會隱藏物件間真正的相依性
  • 以房子類別為例,實例化房子卻傳入整個家的狀態,追建構子才知只要門、窗與屋頂
  • 給房子還要給整個家的物件,導致物件重用性變差
  • 解決方式是不透過定位器,明確將門、窗與屋頂作為參數傳給房子
  • 此舉能讓獨立測試變得容易
  • 服務定位器把查詢與建立物件的功能混淆
  • 還需要額外的介面、覆寫或仿造服務定位器的設定方法(setter)
  • 以致一旦依賴服務定位器,也需要依賴其他跟服務定位器有關的東西
  • 服務定位器有很多別名,本質上都是只需要部分物件但給了全世界
得墨忒耳定律:
  • 以結帳為例,一般來說只會拿錢(直接需要的物件)給店員(請求物件的物件),不會整個錢包(全域狀態)給店員
  • 只請求直接需要的物件
  • a.getX().getY()亦即間接取得物件即違反得墨忒耳定律
  • 好萊塢法則(Hollywood principle, that is "Don't call us, we'll call you")
破解依賴注入的迷思
  • 子物件新增一個參數,則父物件要一路傳遞參數直到給子物件
  • 事實上父物件並不負責建構子物件,父物件並不知道子物件怎麼建構的,所以也沒有傳遞參數的問題
  • 房子類別為例,房子並不知道門有門把,房子只需要建構好的門物件
  • 工廠模式(Factory pattern)負責將物件組合起來,與業務邏輯分開
  • 同一個生命週期的類別們只要同一個工廠組合就夠,並不用一個類別一間工廠,所以不會導致類別倍數增生
  • 注入的物件生命週期應該大於等於被注入的物件,如果有生命週期較短的物件,應該調整架構,將短生命週期的物件分群
偏執狂程式設計(Paranoid programming/coding)
  • 到處是空指標檢查讓測試變得困難
  • 測試房子的油漆功能不需要門,傳入門的空指標,但門的空指標檢查讓測試無法繼續
  • 與其到處用先決條件檢查,不如用許多測試確保程式能跑
  • 如果物件是給外部使用的應用程式介面(API),加入先決條件檢查是合理的
  • 但若給內部使用,一堆先決條件檢查表示對程式的穩定性沒有信心
  • 產品程式碼不應該傳空指標給物件,應該用空集合代替,若希望傳入的集合做事就實作方法,不然就給無操作(NOP a.k.a. NOOP)
  • 測試程式碼可以傳空指標給物件,目的在表明此物件與測試無關
  • 產品程式碼不應該在工廠類別外的類別呼叫new運算元,即業務與建構邏輯混淆
  • 測試程式碼為了實例化小部分的程式做測試可以呼叫new運算元
總結
  • 絕大部分的情況應該避免呼叫new運算元實例化物件,除了最基底的類別或物件圖的葉類別,其餘有與其他類別互動的類別,應該請求物件
  • 將業務類別與建構類別分開,測試會變得容易,因為可以將業務邏輯與建構邏輯分別獨立測試
  • 若將業務與建構邏輯混淆,測試建構邏輯時還不得不跑業務邏輯做的事
Q&A
  • 問:是否建議不需要假(dummy)物件?
  • 答:否,當測試的時候,無關的物件可以傳空物件進行無操作(NOP),空物件是假物件更強的表現形式

沒有留言:

張貼留言