閒談軟體設計:再來一碗

更新於 2024/02/24閱讀時間約 8 分鐘

有點意外,上一篇 閒談軟體設計:來煮碗拉麵吧 回響還不錯,果然標題是很重要的 (咦~重點不應該是內容嗎?),本來這一篇是要來回答和前同事討論中的一個問題,什麼是 Dependency-Inversion Principle?但例子後來弄得有點太複雜了,解釋起來反而有點模糊焦點。

再仔細看了一下,也許可以用來完結先前一直未完成的 library vs. framework 的三部曲,起源是當時 Facebook 有篇文章討論不少人分不清楚上述二者的差別,當時寫了首部曲 閒談軟體設計:API Naming Style,接著是 閒談軟體設計:內部函式庫,但始終沒談到 library 和 framework 的差別,主要是沒有好的例子,這次這例子還蠻不錯的。同樣,一開始不講解理論,先從例子說起。


因為拉麵賣得很好,你的老闆開始想要開另一個海鮮拉麵品牌,注意,是一個新的品牌,不是一家新的分店喔。於是找你規劃,如何把目前成功的方式複製到另一個品牌,這時,你開始思考,怎樣煮好一碗拉麵?

客人點好餐,廚房照著食譜,開始熱碗、準備湯底、煮麵、準備風味油、加入高湯,最後放入配料。

接著,陷入沉思,什麼是目前的品牌有的,但另一個新品牌不會有,或是目前品牌沒有的,但新品牌會有的?

目前品牌有兩種口味:豚骨拉麵和鹽味拉麵,高湯有豚骨湯和雞湯,客人可以調製濃度,湯底則有醬油與鹽味,風味油有豬背油與蔥油,麵是細直麵,分三種不同的硬度;新的品牌暫定只有一種口味,高湯是魚干高湯,客人無法調濃度,湯底則是昆布鹽,風味油是蝦油,麵則是細捲麵,硬度則提供軟、適中、硬和超硬四種。

於是你發現到,高湯和風味油可能會是差最多的,其他流程依然很相似,於是決定把流程的定下來,讓新品牌的主廚來決定實際的內容,並且將高湯和風味油的製作也委託新品牌的主廚。

為了讓新主廚有個可參考的範本,首先,從上面的引述的文字 (亦可稱之為 problem statement,一般是 OOAD 開始的起點,但我蠻懷疑實務上真的有人用這方式嗎?笑),得到一個設計:

raw-image


  • VendingMachine (借用自動販賣機) 處理使用者輸入的選項,轉成訂單
  • RamenOrder 代表使用者的訂餐,會轉成食譜
  • RamenRecipe 代表拉麵的食譜,主要是用 cook() 函式來煮拉麵,餐廳可以覆寫 (overwrite) 已經定義好的幾個函式 (class diagram 是用 abstract class 表示,但實際的範例程式碼則是用 Java 特有的 interface 加 default method)
  • SoupMakerScentedOilMaker 若餐廳有較複雜的湯及風味油調理,可以提供多種實作
  • RamenStore 提供一個 start() 代表開店,不停地接 VendingMachine 產生的訂單,然後煮拉麵。

接著,開始 refactor 原先的拉麵品牌 Okawari (再來一碗) 的程式碼,為了能符合上面的設計,於是提供一個 OkwariVendingMachine 來處理口味、湯底、濃度和、麵的硬度等不同的組合,這裡使用 JCommander 來處理參數的解析 (parsing),於是可以用 -flavor=tonkotsu -c=stronger -hardness=hard 建立一張鹽味豚骨拉麵、湯濃麵應的訂單。

然後,將原先的程式碼 (詳見閒談軟體設計:來煮碗拉麵吧最後 Java 的版本),根據新的設計進行 refactor,若仔細看會發現,大多只是搬來搬去,例如:風味油的調製變成 lambda 物件傳入 recipe 中,在各自的 cook 中在對的順序呼叫 prepareScentedOil,移除不再需要的 preparePorkScentedOil 和 prepareScallionScentedOil,並將高湯、濃度、湯底、麵的硬度、口味、食譜及訂單的選項變成獨立的檔案 (完整程式碼詳見 GitHub repo)。

最後,提供 OkawariRamenStore 程式的入口,用 OkawariVendingMachine 建立一個 RamenStore 的實體,呼叫 start() 就開店了。

一切運作順利,接下來就是換新的品牌準備要開店了。


新品牌 Tako Ramen (小八拉麵店,是的,取自海賊王中的小八) 的主廚在看了範本後,依樣畫葫蘆,建立了一個 TakoVendingMachine,由於只有一種口味、一種高湯和一種湯底,所以販賣機只需要處理麵的硬度:

然後定義,TakoRamenOrder 及 TakoRamenReceipe 等類別。由於,新品牌的廚房導入保溫碗櫃,所以拿出來的時候,碗就是熱的,不用再加熱,因此也不需要呼叫 heatBowl

很快地,小八拉麵店就開張了,老闆相當開心。

看完了上面的例子,如果想要再開一個新的品牌,大家覺得會很難嗎?


在這例子中,okawai-store 的 maven 設定中正好加了兩個 dependencies,一個是 ramen-store-framework,另一個則是 jcommander,也正好分別一個是 framework 一個是 libaray。可能有人會問,看不出差別是什麼啊?

也因為差異沒那麼明顯,加上很多廠商也都稱自家的 library 為 framework (事實上 Apple 的共享套件也稱為 framework),因此漸漸大家也就將兩字交互使用,若真要說差別,大概有兩個:

  • framework 通常會主導流程 (或控制程序),以剛剛例子為例,當呼叫 start() 後,process 的控制權就交給 framework,因為 start() 內部是個無窮迴圈,後面的程式碼再也不會執行,除非 framework 想要結束執行並放棄執行權,這時 process 控制權才又會回到使用 framework 的程式手上。反之,library 不會持續掌握控制權,library 的使用者通常呼叫 library 的函式後會馬上得到想要的結果,並返回控制權。

Java Swing 倒是有點特別,main thread 在執行 JFrame.setVisible(true) 後 會繼續執行後面的程式,Java Swing 會啟動一個 event loop 專用的 thread 處理 UI 的繪製與事件的處理,一般來說,後面通常沒有其他程式,main thread 會結束,JVM 的 process 則是會等到所有的 thread 都結束後才會結束,變成視窗仍在提供服務,但 main thread 其實早已經結束的現象。

  • framework 一般來說會有個解決問題的核心思想,開發者以 framework 建議的方式在 framework 的基礎上加入你想要的功能,像是提供某個介面的實作。以 Java Swing 為例,它便是提供如何處理 GUI 的呈現與事件處理的解決方案,想處理按鈕,可以提供一個 Action 的實作,想呈現表格,可以提供一個 TableModel 的實作。回到拉麵的例子,為 RamenStore 提供 VendingMachine 的實作。反觀,library 比較像是一把瑞士刀,提供多種工具讓開發者可以使用並解決問題,比較沒有一個非得怎麼處理問題的核心思想,例如 Apache 的 Commons IO 提供非常多有用的函式進行 I/O 的存取,但並沒有規定你的應用程式該怎麼寫。

當然,上論兩個差異是我比較 old school 的想法,這年頭,這兩者的差異越來越不明顯了,以 reuse 的角度來說,framework 或是 library 都是開發程式時不可少的重要組成。最後,這裡放上 Wikipedia 的說法作為結束。


本來還要聊 strategy pattern,但拉麵吃多了有點膩,所以下次換個例子來聊聊 strategy pattern 吧!至於原本想聊的 Dependency-Inversion Principle 呢?我得再想想有沒有更好的例子。


完整的範例程式可在我的 GitHub 找到:ramen-store,分成三個目錄 (Maven module),ramen-store-framework 是框架,okawari-store 和 tako-store 則是使用框架的兩個實例。

avatar-img
53會員
104內容數
這是從 Medium 開始的一個專題,主要是想用輕鬆閒談的方式,分享這幾年軟體開發的心得,原本比較侷限於軟體架構,但這幾年的文章不僅限於架構,也聊不少流程相關的心得,所以趁換平台,順勢換成閒談軟體設計。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Spirit的沙龍 的其他內容
我自己偏好用 Repository 搭配 decorator 來管理 cache,而不是在 controller 層或是到處都有快取的邏輯,如果程式都是透過 Repository 更新資料,Repository 就會是一個不錯的地方更新快取,邏輯也就不會散亂在各處了。
提到後端工程師,似乎就只是開發 API,但一個複雜的系統其實不太可能只透過 API 就能完成,例如一個簡單的功能,註冊會員,其實是由好幾個不同類型的工作互相配合,您才能收到開通信,才確保資料庫不會有一堆未開通帳號等。所以今天就來聊聊一個系統有幾種不同執行方式的工作。
最近隨著 FP 的流行,immutability 一直被提倡,物件有狀態,會被修改好像是一種惡,但真是如此?immutability 很好,但所謂的狀態就是會隨著操作變動,差別只在於變動發生在哪裡?
針對這議題,從 devOps 的角度看,團隊應抱有持續不斷地改進的精神,努力降低上版的風險,最後,哪一天上版就僅僅是風險控管的問題了。風險控管除了考量到損失,當然還要考慮到團隊要怎麼 on-call,on-call 的資源夠不夠應付上版後的突發狀況,才能做出適當的決策。
這文章來自網友在 在 Medium 上的留言 (有人幫忙想題目也挺不錯的),問到:Singleton 對於好的架構來說是否能避免就避免呢?我簡單地回了一下我的想法 ,但 Singleton 其實很有趣,所以就寫篇文章來聊聊吧!
真的要符合 single responsibility,通常會得到很多很小的類別或是函式,各別完成一個小的功能,然後在某個地方被聚合起來完成一個使用案例 (use case),而不是一個很大的類別,包山包海,然後最後變成一個狀態超複雜,超級難測試的類別。
我自己偏好用 Repository 搭配 decorator 來管理 cache,而不是在 controller 層或是到處都有快取的邏輯,如果程式都是透過 Repository 更新資料,Repository 就會是一個不錯的地方更新快取,邏輯也就不會散亂在各處了。
提到後端工程師,似乎就只是開發 API,但一個複雜的系統其實不太可能只透過 API 就能完成,例如一個簡單的功能,註冊會員,其實是由好幾個不同類型的工作互相配合,您才能收到開通信,才確保資料庫不會有一堆未開通帳號等。所以今天就來聊聊一個系統有幾種不同執行方式的工作。
最近隨著 FP 的流行,immutability 一直被提倡,物件有狀態,會被修改好像是一種惡,但真是如此?immutability 很好,但所謂的狀態就是會隨著操作變動,差別只在於變動發生在哪裡?
針對這議題,從 devOps 的角度看,團隊應抱有持續不斷地改進的精神,努力降低上版的風險,最後,哪一天上版就僅僅是風險控管的問題了。風險控管除了考量到損失,當然還要考慮到團隊要怎麼 on-call,on-call 的資源夠不夠應付上版後的突發狀況,才能做出適當的決策。
這文章來自網友在 在 Medium 上的留言 (有人幫忙想題目也挺不錯的),問到:Singleton 對於好的架構來說是否能避免就避免呢?我簡單地回了一下我的想法 ,但 Singleton 其實很有趣,所以就寫篇文章來聊聊吧!
真的要符合 single responsibility,通常會得到很多很小的類別或是函式,各別完成一個小的功能,然後在某個地方被聚合起來完成一個使用案例 (use case),而不是一個很大的類別,包山包海,然後最後變成一個狀態超複雜,超級難測試的類別。
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
看到有人因為這次的事情在說這是因為沒有校園沒有髮禁、禁止體罰、延後到校時間導致學生逐漸沒有紀律,覺得自己什麼都能做......我第一個想到的是我爸說他高中時畢業典禮教官只要走得慢一點一定會被憤怒的學生蓋布袋拖去打。發生這樣的事情當然得檢討,但希望不是以一種意氣用事、情緒主導的心態......
Thumbnail
「一群人所決定的,就是對的嗎?」 這是民主的盲點也是缺點
Thumbnail
  關於自己筆名的由來,因為自己不是個擅長口語表達言詞的人,說話時總感覺自己頗詞窮;又因聽聞古人取名,會有取一些相反含意的名字,以期許往後可以免除壞事。故將自己名字的唸音取作「詞窮」,以期未來在創作的路上,文字能夠多變靈活,不要像口語表達的一般糟糕。
Thumbnail
前言須知-《閒人隨筆》 看官沒錯,歡迎來到閒人的復耕系列《閒人隨筆》,沒錯本系列所有作品是閒人將以往作品重新經查證,彙整的新文章。白話說法 補足故事彙整或翻譯失誤等。 今日配合之前的拔劍篇在轉一篇,
Thumbnail
最近身旁有幾位正在懷孕、或剛生產完的朋友,讓我想起自己在懷孕期間印象最深刻的三件「怪事」,其中又以第三件事最誇張。
Thumbnail
不知道大家在買房之前是不是都會參考親朋好友的意見,或是上網看一些買房注意事項,有時候考慮了這塊就忘了那塊,考慮的那塊又忘了這塊.......
纏中說禪,本名李彪,專欄筆名木子,其人是中國股市比較早期的操盤手,所以他比較熟悉a股的市場情況。他以“纏中說禪”為筆名,從2002年開始寫博客,直到2008年癌症病重停更,期間寫下了不少文章。而博客文章中最為著名的就是他的“教你炒股票”系列文章,他在這個系列裡講到的炒股理論和方法被粉絲稱為“纏論
Thumbnail
婚姻是人生大事,對溥儀尤其如此,因為如果皇帝大婚,就代表溥儀可以脫離眾多便宜老媽的束縛而得以親政。 但詭異的是,這個可以讓他脫離便宜老媽掌控的婚姻,卻還是要由便宜老媽進行主導並且居中角力......
Thumbnail
上次我提到:溥儀就是個死小孩。其實這不能全怪溥儀,而要怪詭異的宮廷教育及生活制度......
Thumbnail
近期電影「末代皇帝」重新修復上映。 為了推坑這部經典之作,本人決定以溥儀本身的自傳《我的前半生》為主要基底,和大家談一些電影中礙於篇幅或是藝術改編,而不容易察覺或是沒有呈現的真實歷史。
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
看到有人因為這次的事情在說這是因為沒有校園沒有髮禁、禁止體罰、延後到校時間導致學生逐漸沒有紀律,覺得自己什麼都能做......我第一個想到的是我爸說他高中時畢業典禮教官只要走得慢一點一定會被憤怒的學生蓋布袋拖去打。發生這樣的事情當然得檢討,但希望不是以一種意氣用事、情緒主導的心態......
Thumbnail
「一群人所決定的,就是對的嗎?」 這是民主的盲點也是缺點
Thumbnail
  關於自己筆名的由來,因為自己不是個擅長口語表達言詞的人,說話時總感覺自己頗詞窮;又因聽聞古人取名,會有取一些相反含意的名字,以期許往後可以免除壞事。故將自己名字的唸音取作「詞窮」,以期未來在創作的路上,文字能夠多變靈活,不要像口語表達的一般糟糕。
Thumbnail
前言須知-《閒人隨筆》 看官沒錯,歡迎來到閒人的復耕系列《閒人隨筆》,沒錯本系列所有作品是閒人將以往作品重新經查證,彙整的新文章。白話說法 補足故事彙整或翻譯失誤等。 今日配合之前的拔劍篇在轉一篇,
Thumbnail
最近身旁有幾位正在懷孕、或剛生產完的朋友,讓我想起自己在懷孕期間印象最深刻的三件「怪事」,其中又以第三件事最誇張。
Thumbnail
不知道大家在買房之前是不是都會參考親朋好友的意見,或是上網看一些買房注意事項,有時候考慮了這塊就忘了那塊,考慮的那塊又忘了這塊.......
纏中說禪,本名李彪,專欄筆名木子,其人是中國股市比較早期的操盤手,所以他比較熟悉a股的市場情況。他以“纏中說禪”為筆名,從2002年開始寫博客,直到2008年癌症病重停更,期間寫下了不少文章。而博客文章中最為著名的就是他的“教你炒股票”系列文章,他在這個系列裡講到的炒股理論和方法被粉絲稱為“纏論
Thumbnail
婚姻是人生大事,對溥儀尤其如此,因為如果皇帝大婚,就代表溥儀可以脫離眾多便宜老媽的束縛而得以親政。 但詭異的是,這個可以讓他脫離便宜老媽掌控的婚姻,卻還是要由便宜老媽進行主導並且居中角力......
Thumbnail
上次我提到:溥儀就是個死小孩。其實這不能全怪溥儀,而要怪詭異的宮廷教育及生活制度......
Thumbnail
近期電影「末代皇帝」重新修復上映。 為了推坑這部經典之作,本人決定以溥儀本身的自傳《我的前半生》為主要基底,和大家談一些電影中礙於篇幅或是藝術改編,而不容易察覺或是沒有呈現的真實歷史。