閒談軟體設計:再來一碗

更新於 發佈於 閱讀時間約 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
留言分享你的想法!
avatar-img
Spirit的沙龍
53會員
104內容數
這是從 Medium 開始的一個專題,主要是想用輕鬆閒談的方式,分享這幾年軟體開發的心得,原本比較侷限於軟體架構,但這幾年的文章不僅限於架構,也聊不少流程相關的心得,所以趁換平台,順勢換成閒談軟體設計。
Spirit的沙龍的其他內容
2024/03/23
這篇文章探討了在軟體開發中的技術債可能來自哪些原因,以及如何自動化偵測與修復技術債。作者透過分享不同情境下的技術債選擇,提供了對於技術債的思考與建議,針對開發人員在需要做出無奈的技術決策時,提供了一些建議。此外,還提供了一些在做出技術決策時的方法,如保留抽象層和避免vendor lock-in。
Thumbnail
2024/03/23
這篇文章探討了在軟體開發中的技術債可能來自哪些原因,以及如何自動化偵測與修復技術債。作者透過分享不同情境下的技術債選擇,提供了對於技術債的思考與建議,針對開發人員在需要做出無奈的技術決策時,提供了一些建議。此外,還提供了一些在做出技術決策時的方法,如保留抽象層和避免vendor lock-in。
Thumbnail
2024/03/09
今天來聊個最近很夯的主題 DDD,但不是 DDD 的本尊 Domain Driven Design,而是無所不在的 Database Driven Design,Database Driven Design 不是不好,只是你的模型容易變成貧血模型,邏輯都集中在 service 層等等。
Thumbnail
2024/03/09
今天來聊個最近很夯的主題 DDD,但不是 DDD 的本尊 Domain Driven Design,而是無所不在的 Database Driven Design,Database Driven Design 不是不好,只是你的模型容易變成貧血模型,邏輯都集中在 service 層等等。
Thumbnail
2024/03/02
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
2024/03/02
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
看更多
你可能也想看
Thumbnail
大家好,我是一名眼科醫師,也是一位孩子的媽 身為眼科醫師的我,我知道視力發展對孩子來說有多關鍵。 每到開學季時,診間便充斥著許多憂心忡忡的家屬。近年來看診中,兒童提早近視、眼睛疲勞的案例明顯增加,除了3C使用過度,最常被忽略的,就是照明品質。 然而作為一位媽媽,孩子能在安全、舒適的環境
Thumbnail
大家好,我是一名眼科醫師,也是一位孩子的媽 身為眼科醫師的我,我知道視力發展對孩子來說有多關鍵。 每到開學季時,診間便充斥著許多憂心忡忡的家屬。近年來看診中,兒童提早近視、眼睛疲勞的案例明顯增加,除了3C使用過度,最常被忽略的,就是照明品質。 然而作為一位媽媽,孩子能在安全、舒適的環境
Thumbnail
我的「媽」呀! 母親節即將到來,vocus 邀請你寫下屬於你的「媽」故事——不管是紀錄爆笑的日常,或是一直想對她表達的感謝,又或者,是你這輩子最想聽她說出的一句話。 也歡迎你曬出合照,分享照片背後的點點滴滴 ♥️ 透過創作,將這份情感表達出來吧!🥹
Thumbnail
我的「媽」呀! 母親節即將到來,vocus 邀請你寫下屬於你的「媽」故事——不管是紀錄爆笑的日常,或是一直想對她表達的感謝,又或者,是你這輩子最想聽她說出的一句話。 也歡迎你曬出合照,分享照片背後的點點滴滴 ♥️ 透過創作,將這份情感表達出來吧!🥹
Thumbnail
※ 原本狀態:伺服器渲染 這是 MVC 架構下的 request / response 示意圖,在這張圖呈現的架構裡,畫面和資料都由同一個架構處理。 伺服器渲染流程: 瀏覽器針對特定網址送出請求。 路由器解析請求後,轉接給對應的 controller。 controller 按照要求,透過
Thumbnail
※ 原本狀態:伺服器渲染 這是 MVC 架構下的 request / response 示意圖,在這張圖呈現的架構裡,畫面和資料都由同一個架構處理。 伺服器渲染流程: 瀏覽器針對特定網址送出請求。 路由器解析請求後,轉接給對應的 controller。 controller 按照要求,透過
Thumbnail
樣板模式的定義極為簡單,卻是大型系統程式、WEB/APP應用框架的設計核心,完美展現設計模式的價值: 簡單、高效、強大。
Thumbnail
樣板模式的定義極為簡單,卻是大型系統程式、WEB/APP應用框架的設計核心,完美展現設計模式的價值: 簡單、高效、強大。
Thumbnail
本書大多數的內容都以 OO 的概念出發,詳列了許多設計的臭味道,也有大量的例子。個人雖然不會這樣寫程式,但仍是覺得受益良多,至少在 code review 時能更清楚知道該怎麼描述問題。不過,即便不是用 OO 的概念,有些章節還是可以帶來一些想法,用 OO 概念寫程式的人更不該錯過這本好書。
Thumbnail
本書大多數的內容都以 OO 的概念出發,詳列了許多設計的臭味道,也有大量的例子。個人雖然不會這樣寫程式,但仍是覺得受益良多,至少在 code review 時能更清楚知道該怎麼描述問題。不過,即便不是用 OO 的概念,有些章節還是可以帶來一些想法,用 OO 概念寫程式的人更不該錯過這本好書。
Thumbnail
實際就業後,會發現收集與分析需求,通常都不是工程師在做,會有另一群人,以非工程的角度收集及分析需求,然後在開發過程中蹦出不同的火花,於是很好奇另一群人的想法是什麼?我不敢說這本書能完全代表另一群人的想法,但確實能夠得到很多有用的思維。推薦給所有的軟體工程師。
Thumbnail
實際就業後,會發現收集與分析需求,通常都不是工程師在做,會有另一群人,以非工程的角度收集及分析需求,然後在開發過程中蹦出不同的火花,於是很好奇另一群人的想法是什麼?我不敢說這本書能完全代表另一群人的想法,但確實能夠得到很多有用的思維。推薦給所有的軟體工程師。
Thumbnail
本書介紹了戰略設計、管理領域複雜度、實際應用領域驅動設計等主題。透過對核心子領域、支持子領域、限界上下文等概念的探討,提供了領域驅動設計的相關知識。這篇文章中還涉及了微服務、事件驅動架構和資料網格等相關主題,提供了設計系統和應用領域驅動設計的指導。
Thumbnail
本書介紹了戰略設計、管理領域複雜度、實際應用領域驅動設計等主題。透過對核心子領域、支持子領域、限界上下文等概念的探討,提供了領域驅動設計的相關知識。這篇文章中還涉及了微服務、事件驅動架構和資料網格等相關主題,提供了設計系統和應用領域驅動設計的指導。
Thumbnail
軟體系統的發展歷程大多相似,首重解決基本需求、提供操作介面,進而提升安全性、擴充功能、優化操作。
Thumbnail
軟體系統的發展歷程大多相似,首重解決基本需求、提供操作介面,進而提升安全性、擴充功能、優化操作。
Thumbnail
列出一套完整的程式 程式設計有許多種方法,不過通常會先列出清單的再逐一執行,這樣會加快程式設計的速度。設計通常會採取順推的辦法。所以順推的程式設計方式就是經歷觀念溝通、系統分析、資料統合、權限管理、頻率與時間、後台管理、畫面設計等等階段後,將框架設計完了以後,先列出一套完整的程式,將所有使用者都確
Thumbnail
列出一套完整的程式 程式設計有許多種方法,不過通常會先列出清單的再逐一執行,這樣會加快程式設計的速度。設計通常會採取順推的辦法。所以順推的程式設計方式就是經歷觀念溝通、系統分析、資料統合、權限管理、頻率與時間、後台管理、畫面設計等等階段後,將框架設計完了以後,先列出一套完整的程式,將所有使用者都確
Thumbnail
程式設計中不可或缺的一部分 介面是使用者與程式互動的媒介,因此介面的設計會影響使用者的體驗和感受。一個清晰明白、易懂的介面,可以讓使用者輕鬆地使用程式,並獲得良好的使用體驗。 需要與程式設計師密切溝通 設計師需要了解程式的功能和需求,並根據使用者的習慣和需求進行設計。設計師和程式設計師之間的溝
Thumbnail
程式設計中不可或缺的一部分 介面是使用者與程式互動的媒介,因此介面的設計會影響使用者的體驗和感受。一個清晰明白、易懂的介面,可以讓使用者輕鬆地使用程式,並獲得良好的使用體驗。 需要與程式設計師密切溝通 設計師需要了解程式的功能和需求,並根據使用者的習慣和需求進行設計。設計師和程式設計師之間的溝
Thumbnail
替產業做設計 有人要我談程式設計,那我就稍微談一下。我從事的大都是產業的工作,所以我們也從如何替產業做設計來談起。基本上,每個產業都會有自己的作業流程,大同小異。但是基礎來做都是一樣的,都會有客戶、物料、產品、供應商、員工等資料。不同的是,由於企業型態的不同,他們每個人有不同的作業流程。這個作業流
Thumbnail
替產業做設計 有人要我談程式設計,那我就稍微談一下。我從事的大都是產業的工作,所以我們也從如何替產業做設計來談起。基本上,每個產業都會有自己的作業流程,大同小異。但是基礎來做都是一樣的,都會有客戶、物料、產品、供應商、員工等資料。不同的是,由於企業型態的不同,他們每個人有不同的作業流程。這個作業流
Thumbnail
起源是當時 Facebook 有篇文章討論不少人分不清楚上述二者的差別,當時寫了首部曲《閒談軟體設計:API Naming Style》,接著是《閒談軟體設計:內部函式庫》,但始終沒談到 library 和 framework 的差別,主要是沒有好的例子,這次這例子還蠻不錯的。
Thumbnail
起源是當時 Facebook 有篇文章討論不少人分不清楚上述二者的差別,當時寫了首部曲《閒談軟體設計:API Naming Style》,接著是《閒談軟體設計:內部函式庫》,但始終沒談到 library 和 framework 的差別,主要是沒有好的例子,這次這例子還蠻不錯的。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News