避免過早的抽象設計

這是我的一則臉書貼文的延伸,只是希望把想法整理得更清楚、更完整一點。


原本的臉書貼文在這裡:Huanlin 學習筆記臉書專頁


當初會發那則貼文,只是有感而發,沒想要 diss 誰。只是有點擔心……也許我多慮吧了。但既然起了個頭,我覺得有義務要把想法說得更清楚一點(強迫症?XD),畢竟一帖臉書很難把這個議題和想法講得詳細,恐怕導致每個人閱讀時,自動投射到自己目前的專案和既有經驗上,繼而對我原先想要表達的想法有所誤解。

TLDR;

這裡我會先轉貼臉書的貼文和一則留言,然後摘錄 Uncle Bob 大約四個月前的受訪談話來作為補充。他的談話(如下圖)可能會令你感到驚訝。該訪談的影片連結也會一併附上,以免我片面誤解或斷章取義。



要先說的是,Uncle Bob 的那段訪談大約是四個月前(2024-04-30)發布,故我認為應該能夠代表 Bob 大叔最新的想法——除非他為了避免跟主持人辯論交鋒而刻意「圓融地」順著主持人的立場來回答。


OK,開始吧。

過早的抽象

我看到一篇標題為〈使人瘋狂的 SOLID 原則:開放封閉原則 (Open-Closed Principle)〉的文章,作者認為 OCP 的用意是告訴我們「應該要朝著就算加了三四五六七個新的功能,我們還是不會影響到原有的程式,更能優雅地設計著。」

不知道現在是否仍有很多人朝這個方向努力呢?

這裡不是要 diss 特定文章或作者。就我所知,大部分 SOLID、Clean Code 的支持者都有類似的理解和建議。故這裡要說的,是針對這類「可能偏向過早設計抽象層」的建議所提出的反思。

我曾這麼相信,也試著盡量去實踐。例如 Dependency Injection,我也曾用力學習和試著把它用對。但老實說,根據我自己的經驗,我逐漸覺得 SOLID、DRY、 Clean Code 這些,似乎是有點被過分強調,或者 over rated 了。


我的意思是,作為提醒和一種工具,用它們來改善軟體的設計(當它需要改善時),以及讓新手知道有這些概念,是挺好的。但如果要經常去想:


「即使增加了三四五六七個功能,還是不會影響到原有程式。」......


No, thanks.


我試過,然後在懷疑中逐漸停止這麼做,以免在不斷燒腦和想像未來的變動當中把程式的複雜性越弄越糟:生出一堆抽象層、間接又再間接、想方設法拆散然後以某種方式連接......如此反而耽誤了產出,且增加太多無謂的複雜性和日後的維護成本。


難道過程中不會有收穫嗎?肯定多少有的,但我覺得這彎路會不會繞得遠了點,費力了點?人生的時間寶貴,務實一點好。


當我們的程式真的碰到需要改動(調整需求或架構)時再來重構。趁重構的時候再來思考是否需要引入更多抽象層或設計模式,通常不會太遲。Martin Fowler 不是有「三次重構原則」嗎?這個就很好,簡單、明確、務實。重點是它真的有幫助,不會令人流淚。

轉貼臉書留言

上一節的內容幾乎是臉書貼文的全文,僅稍作潤飾與排版。這裡要順便轉貼一位朋友在該貼文下方的留言(已事先徵得同意),因為我覺得這樣的經驗分享很值得參考,而且我也有類似的經驗。


==== 轉貼開始 ====

(點頭如搗麻糬)

小弟這些年維護了幾個不同的上十幾年的老系統,有不做任何軟體架構一路由上往下執行的。也有把各種軟體架構 practice 都用上一些的。有限的經驗感覺下來,要增加功能或是重構,在兩者都沒有文件與不知清楚商業邏輯跟流程的狀況下,是前者好改許多。


我維護到後者的嚴重的狀況是在專案裡分了許多層,資料庫,驗證,錯誤,商業邏輯等等都各自分層嚴格封裝,但無奈的是沒有用 DI,程式非常難閱讀,也很難掌握資料流程等等。試著讀程式跟資料表想理出沒有文件的商業流程已經很辛苦,還要面對這些 practice 非常累人。我常感受到的焦慮的是...我接觸的專案作者都已離開。留下的code 蠻常有似是 DRY 但為了DRY 而拐到自己的腳的架構。


目前做軟體專案,第一個想到盡量使用只有DI,其他的.. SOLID DRY 等等軟體架構我自己都做得不好(所以狂點頭) 😛 反正我三不五時就會整理到幾週前自己做的蠢架構

==== 轉貼結束 ====


接著是 Uncle Bob (Robert C. Martin) 的一段訪談摘錄。他的相關著作包括:Clean Code、Clearn Coder、Clean Architecture、Clean Agile 等等。

Uncle Bob 訪談

訪問 Uncle Bob 的主持人自稱 Primeagen(這名字不知是否源自什麼梗)。就我的了解,他大多是站在 Clean Code 和 SOLID 原則的對立面。


訪談影片的發布日期是 2024-04-30,影片長達一個小時,我僅摘錄其中一小段。有興趣的朋友可以點此連結查看 Youtube 影片,以免我誤譯或斷章取義。若發現我有誤解,也請不吝指正。


以下對話摘錄是從影片的 17:00 開始,至 18:30。


==== 摘錄開始 ====

主持人:有些人或者你本人對軟體設計的建議是在開始動手寫程式之前,先建立抽象(abstract)並思考程式碼的結構,我的理解正確嗎?


Bob: 是的。有一些附加前提,但沒錯。


主持人:好的。有些現代觀點,或者至少是 Go 語言的觀點,卻幾乎與那樣的看法背道而馳,也就是先不去處理抽象,而是先把東西做出來,然後讓抽象層逐漸浮現出來。你會怎麼解決這兩種學派的差異呢?


Bob: 我不認為有解方。所以我會先處理最具體的(concrete)東西。我會先思考問題……也許我會先在白板或紙上畫個設計草圖之類的,然後我會盡快著手處理實際的(concrete)問題。然後,我會讓實際的問題迫使我找出抽象的部分。因為當我的 code 越加越多,會令我覺得如果不加個抽象層來應對,就很難繼續加入更多 code。換言之,程式碼迫使抽象層在我心中浮現,然後,我會反覆持續這個程序,最終便會生出很多抽象層。

==== 摘錄結束 ===


接著,在影片大約 20:20 處,主持人問 Bob 大叔是否有過這樣的經驗:先設計抽象的部分,最終感到後悔。


Bob 的回答是:「在我年輕的時候。(大笑)……那是艱難的教訓……隨著年齡增長,我盡可能保持具體(concrete),直到具體的世界迫使我改變(按:迫使從具體中找出抽象部分)。」


暫且摘錄到這裡吧。影片很長,裡面還有討論到 Strategy Pattern(非常好用的模式!)、UML 作為前置設計(Bob 大叔:「that never worked」)、Agile、Scrum Master、TDD、單元測試與整合測試……等等。

結語

當我看到 Bob 大叔的回答,我有點驚訝……那不就是我臉書貼文想要表達的嗎?至少是非常接近的。不知道您同意嗎?又或者只是我的誤讀和誤解呢?


這個訪談影片是大約四個月前上傳的,故應該可以說,比較能呈現 Uncle Bob 最新的想法——除非他因為想避免跟主持人衝突而刻意順著對方的話來講一點違心之論(若是那樣,也太「圓滑」了吧)。


這個議題看似已有明顯的學派之分,而且很容易上升到哲學層次(然後越講越抽象 XD),就算我在這裡嘗試對之前的臉書貼文做一些補充,也不見得能解釋得夠清楚。不過,網路上還有更多文章和影片在談這個議題,有興趣的朋友不妨參考多方意見,並結合自己的實際開發經驗來驗證看看哪一種學派比較適合自己的軟體開發旅程。


經驗很重要。無論哪個學派,只要有心,認真去做軟體設計和開發,最終都能獲得一些經驗。只是有個問題可能也得想想:「什麼樣的經驗?走多遠的路才得到的經驗?」

預測未來變動的能力?

我覺得如果已經「確知」需要某種抽象層來避免很可能發生的需求變動,預先加入這個抽象層是合理的。


然而,若將這類預想進一步擴大實施,每一次剛開始設計就加入一些預先推想的抽象層,這恐怕有個問題:忘了自己並沒有「預測未來的能力」,因而設計出一堆其實根本不會用到的抽象層,只因為擔心未來某種可能(但不見得會出現)的變動。


每一個抽象層都是有成本的,而疊床架屋所衍生的維護成本通常不低,故強調「只有確知需要的時候才加入」。我認為有豐富開發經驗的人應該會比新手更能判斷加入哪些抽象層是必要的,但如果對任何人宣稱運用 OCP 或 SOLID 就能夠「即使增加了三四五六七個功能,還是不會影響到原有程式。」卻沒有提醒加入無謂的抽象層反而會增加日後的維護成本,也不知道這個領域還有 YAGNI 和 KISS 原則,那麼當程式從 green field 進入 brown field,恐怕會碰到很多麻煩。


先這樣吧。之後想到甚麼再來補充。

Keep learning!


沒有留言:

技術提供:Blogger.
回頂端⬆️