這篇又浮上來了
method 加上參數,裡面多寫個 if 進行擴充 -- 這大概工程師每天都在做
所以我覺得這類型的還好,它要累積到一個程度之後,才要重構,看是要回到重覆或是抽得更多層。
出現問題的大部份都是太多參數,就是作者說的,抽象化錯了方向。另一種錯誤是太早將單純的 if 抽成更複雜的 class/interface,也不能說一定錯,但新需求多半不可預知,所以八成都是抽錯方向,不如不抽。
然後更難得問題是,這些修改不一定是同一個人做的…
這對我來說一直很兩難,原本單純易懂的 if-else 抽成複雜很難 trace 的 class/interface,究竟是真的有幫助還是炫技?
所以要寫 test
如果比較好測,那通常是比較好的設計。你通常也只會重構到測試不會太難寫這樣,不會太過頭。(class 太多就測試更多,想到這你就會懶得抽這麼多層了)
測試可以引導設計,再加上一些經驗的累積大概不會錯太多。
當然如果寫的是 library ,會放出來給別人用,那又是另一種規則。
我想的方向不太一樣。有一種重複,是像開發 Android app 這樣,每一個開發 app 的人都要繼承 Activity (或 Service etc),並部分 overwrite parent 特定的實作。當很多使用 API 的開發者都在一樣的地方做相同的 overwrite 時,也可以看作是重複。
不當的抽象可粗分為二,一為對抽象對象的錯誤解讀造成的不正確的抽象架構設計或不貼切的描述,另一種是為了避免重複而做的過分集中的抽象。我們當然能避免重複,而不重複的方法就是在抽象層做統一的描述,但這同時也造成了收斂。收斂和僵化是一體兩面,因此收斂的同時也減少了彈性;實務上的影響就是未來擴充的東西,一定要能夠被抽象描述表達,這其實是一種畫地自限。
舉個簡單的例子,幾乎所有繼承 Activity 的 app 都要 call onCreate() 的 super(),這也是一種重複,那為何不在 Activity 中就統一做掉?相信這除了是原設計 Activity 的人,希望能在 Activity class 中擁有足夠凝聚的描述,也是希望使用 API 的開發者能對 Activity 的使用有一個制式的規格可以遵循。
建構程式就像拼拼圖,抽象集中的程度好比拼圖塊的大小,較集中的抽象就是較大塊的拼圖,而拼圖塊如果都太大,可能就沒辦法拼成想要的樣子。但拼圖塊太碎,也同樣不好用,而且經驗淺的程式設計師面對太小的碎塊可能會無所適從。我覺得要做出好的取捨的心法是,朝兼顧彈性、易用性但又不鬆散的方向做設計,而不是單純考量如何讓抽象層最凝聚、濃縮 (過度抽象) 或讓使用者做最少操作 (彈性最差)。