培養軟體架構思維,拒絕過度設計——軟體設計模式精通之旅心得

前言

從去年八月左右開始參加一堂水球軟體學院所開設的「精通軟體設計模式之旅」線上課程,至今也算是完成了一個里程碑,想說是時候可以來分享一下從參加旅程至今的點點滴滴,從參加動機、課程體驗到學習心得,算是為自己這段學習做一個回顧性的紀錄。

參加動機與課程體驗

大概在一年多前從網路社群上面得知有這樣一個課程準備展開,但那時候還只有推出「免費試吃」課程,填寫表單就可以排隊抽籤,抽中的才有參加試吃課程的資格。結果一直沒被抽中,後來終於等到了第一梯次正式收費的課程開跑就直接報名了。所以當初到底是什麼吸引了我,讓我這麼想報名這個課程呢?

衝著老師和挑戰題而來

其實在接觸課程之前,就曾經在課程老師水球潘的 YouTube 頻道看過他講解的設計模式之路系列影片, 當時第一次覺得居然有人有辦法把設計模式的概念講解得這麼有趣,讓人會想要像看劇一樣繼續追下去。不過因為該系列影片似乎因為水球本人的課業因素所以後來停更了,覺得可惜之外,也只能期待哪天他會突然開始更新。結果後來在社群看到他本人發文推出新課程時,真的是就像是追許久的 YT 沈寂一陣子後突然復出一樣的驚喜!

此外,課程中除了教學影片,還包含許多實戰題作業。對我來說,這一點真的是一個很大的吸引力。在自已過去的軟體開發經歷中,很多時候幾乎是透過動手實作才真正理解一個概念,所以我認為實作在學習軟體開發相關技能時是很重要的一環。 同時,由於以往對設計模式的理解可以說僅止於知道幾個名詞,雖然曾看過相關書籍,但因為平常也沒有什麼應用場景或實作的機會,覺得是時候該把這塊補起來。

旅程中不錯的服務

另外還有一些旅程所提供的服務,是我在旅程當中逐漸體會到也很重要的部分:

協助檢驗學習成果

輸出的結果,往往也需要經過檢驗,才能確保有吸收到正確的知識。課程當中會安排每週固定時間的「TA Hour」來針對作業進行 Code Review,除了檢討作業之外,若過程中有遇到什麼問題或困難,都能夠在 TA Hour 中提出,並得到立即的反饋。

活躍的學習社群

以往參加過某些線上課程,有的也是會主打塑造學習社群,但可能剛好我參加過的課程在這部分都跟想像中有差距,學員之間實際上並不會有太多互動,因此一開始對於這部分沒有抱太大期待。初期也真的幾乎是單打獨鬥,頂多有時候有問題在群組發問,有滿多熱心大大會回應,但此時的互動性也不高(對於一個害羞內向的人(會被打ㄇ,應該不會吧這是我的部落格欸)來說,不算是個能很自在的發言的場所)。但後來推出了所謂的「公會系統」,讓學員(在旅程中都稱為冒險者)能夠自由組成學習小組,能夠彼此討論作業或分享進度等等,於是我就順勢加入了其中一個公會,認識了好幾個厲害的夥伴,這才真的體會到互相交流成長的感覺,有人一起寫作業真的會比較有動力!

以上這兩點我後來想想真的算是滿重要讓我可以持續課程的部分,因為以前兩點(1.老師教學品質高 2.豐富的實作練習)來說,我就曾經上過 Berkeley 一個很有名的線上公開課程 CS 61B,內容是真的非常優質,也有配合大量的練習題,但我最後還是沒能完課(大概上到一半而已🥲)。

當初沒有繼續的主因當然是我就懶嘛,但到了軟體設計模式精通之旅,多了有人協助 Code Review 檢驗學習成果+有同儕能互相交流討論,居然可以讓我這種會半途放棄的人繼續上課寫作業,可見這兩點真的是非常有幫助的!

學習心得

重新認識物件導向,從 OOA 開始

這門課雖然稱之為「設計模式精通之旅」,但並沒有像一般大部分書籍或教學劈頭就把 GoF 的 23 個設計模式全部丟給我們,反倒是從重新認識物件導向開始,以及重新釐清「物件」和「類別」的概念,還有「類別」之間的關係。而這一切,都是為了要先打好基本功,也就是在做設計之前的第一步,先建立出「領域模型」,將 UML 類別圖畫出來,捕捉各個物件/類別的屬性和行為,並且標示他們之間的關聯,而這階段也稱之為 OOA(Object-Oriented Analysis)/物件導向分析。

進行 OOA 繪製 UML 類別圖,將各個物件/類別的屬性和行為定義出來,並且標示他們之間的關聯 ⬆︎進行 OOA 繪製 UML 類別圖,將各個物件/類別的屬性和行為定義出來,並且標示他們之間的關聯

OOA 是整門課當中最基礎也最重要的一步,因為當有了 OOA 我們才能進行接下來的設計,也就是 OOD(Object-Oriented Design)/物件導向設計,而這時才是設計模式「可能」會出場的時候。為什麼這邊會說「可能」?因為設計模式並不是生來就讓你想套就套的,當然這樣也不是不行,只是通常這種毫無理由套用設計模式的行為,通常會被認為是「過度設計/Over Design」的,也是課程中很常呼籲大家不要一不小心成為了軟體酒駕過度設計師。

OOD 設計要點:覺察 Forces,得出 Problem,找到 Pattern(Solution)

先回到一開始的定義,所謂的模式,就是一套可以解決特定情境底下的問題的方法,且這套方法經過前人反覆驗證並被傳承下來,白話文就是解決特定問題的一套 SOP。每個設計模式背後都是為了解決某個 特定情境(Context)底下的問題(Problem) 而生,所以說假使問題不存在,那麼也就沒有套用設計的必要。如果再將這些所謂的問題一一攤開來看,會發現其實正是因為一道道 Forces 彼此相互衝突,進而讓問題呼之欲出,此時才會需要透過特定模式(Pattern)的 Form 來解決問題——找到解決方案(Solution),最後得到套用模式之後的結果(Resulting Context)成功化解衝突的 Forces。

這裡所指的 Forces,舉例來說像是「未來可能會繼續擴充新的行為」、「對程式碼維護性的要求」、「須滿足特定的商業邏輯」,每一項都是一道 Force,而多道 Forces 若同時存在又彼此互相衝突,就會令工程師感受到滿滿的壓抑感。

OOD:以為 OOA 基礎,察覺彼此衝突的 Forces 之後定義出 Problem,並找到 Problem 對應的 Solution,套用 Pattern 後的 Resultinng Context

⬆︎OOD:以為 OOA 基礎,察覺彼此衝突的 Forces 之後定義出 Problem,並找到 Problem 對應的 Solution,套用 Pattern 後的 Resultinng Context

『如果沒有察覺到 Forces,就說明沒有設計難題 Problem,也就不必要討論 Solution,更沒必要思考 Pattern。』

唯有精準的察覺到每一道彼此衝突的 Forces 後,才能看出問題所在,再從腦袋中索引該問題適合的解決方案,最後一步才是套用設計模式。我認為真正困難的點都是在察覺 Forces,尤其在做魔王題時,偶爾會有感受不到 Forces 的狀況,這種時候通常都是由於沒有把需求看仔細,也就是在做 OOA 時,可能漏捕捉了某些重要的元素或行為。但連作業都會有這種情況發生了,更何況是現實的商業開發環境,作業因為是刻意要訓練我們,一定會暗藏 Forces,且需求說明這麽完整,我硬找出來即可;但現實當中,我怎麼可能知道哪些是暗藏的?常常連需求都不清不楚了。不過這也代表察覺 Forces 的能力還需要更進一步的提升,在未來面對實際需求時,才更有可能派得上用場。

OOP 就是按照 OOAD 藍圖施工

最後,終於得到了完成 OOD 的 UML 類別圖版本,這時候才真的要開始動手寫程式,進入 OOP(Object-oriented programming)/物件導向程式開發的階段。這時候基本上不管使用什麼語言,都可以照著 OOD 這張藍圖,快速地去完成程式實作的部分。

按照這個 SOP (OOA → OOD → OOP) 進行幾次作業之後,明顯感受到大多數思考程式架構的部分都是在 OOA 與 OOD 階段,而進入 OOP 後,通常都是開始進入碼農模式(除了一些比較複雜的題目,要實現特定演算法或是處理非同步機制需要比較多思考),就像工人依照建築藍圖去施工一樣。

掌握軟體設計精髓,避免過度設計

我想 OOAD 的部分,正是軟體設計的精髓所在,這也是我在過往開發經歷中幾乎沒有培養過的能力,畢竟自己平常工作上大多是 CRUD,手邊大概 80% 以上的工作都是運用框架或工具即可處理的業務需求,如果當初要叫我寫一個 Domain Logic 非常豐富的卡牌遊戲,我可能根本寫不出來。而旅程當中已經寫過了比大小、大老二和 RPG 遊戲,未來還會繼續挑戰到 IoC 容器框架、Web Framework 等等難度更進階的題目,雖然不確定自己能否走到最後一關(但願),不過從現回頭看,自己的軟體設計技能已經成長了不少。

一開始的我純粹只是想學習如何使用設計模式,卻完全忽略了「爲什麼要學習/使用設計模式」,直接在旅程的一開始就被點出了自己從未思考過,卻非常核心的問題。要是沒有參加旅程,我可能就會繼續用以往的思維,只是因為熟悉某些設計模式或單純為了炫技而套模式。而就以一個平常大多數使用框架和工具就能完成日常 80% 工作的人來說,設計模式確實很少機會派得上用上(大多數會使用模式的部分,框架或工具都幫我們做掉了),但我認為這趟旅程所帶來最大的收穫就是對於 OOAD 的掌握能力,同時培養了軟體架構思維,懂得如何避免過度設計,光這一點就讓我覺得非常值得了。