方格精選

閒談軟體設計:發生關係

閱讀時間約 8 分鐘
圖片來源:www.freepik.com

圖片來源:www.freepik.com

上一篇閒談軟體設計:友善的距離意外在 Facebook 上引起不少人的回應 (是的,我不但偷偷加了副標,還擴充了些內容),不過首先要澄清一下,接下來一系列文章 (如果能堅持住繼續寫的話),我不會討論哪個架構比較好,情境不同,本來對架構的選擇就不同,架構師本身也有所謂的偏好,所以討論哪種架構比較好,這種吃力不討好的筆戰,只是自找麻煩。因此,我只是把在一個抽象的架構中實作上可能會遇到的技術選擇或是設計選擇的種種因素放在文章中讓大家思考,嗯,通常我也不會寫答案,頂多是某些過去從經實驗的經驗分享。

關係無法避免

在落落長的引言結束,還是回到本文來吧!如果覺得這篇文章的副標題有點エロ的人,應該都是被標題騙進來的,如果是被騙進來的,可以按上一頁離開,或繼續看完假裝自己不是XD,好啦,玩笑開完了。不管用哪種語言開發軟體,除非是那種一個 function 寫個幾萬行的人 (來人啊,把這種人拖去砍了),不然,一般都會根據某些因素,切割成模組或是特定功能的區塊 (一個 class 或是一個 function),但要完成一個特定功能,這些模組或區塊勢必要一起合作,因此這些模組與區塊就發生了關係

以目前主流的語言來說,大多可以用物件的方式描述可被使用的區塊,UML 的 class diagram 將物件之間的關係分成兩大類:實體階層的關係與類別階層的關係,實體階層有 dependency、association、aggregation 和 composition 四種,類別階層有 generalization 和 realization (implementation) 兩種,共計六種,雖說是六種,但在不同語言哩,實現的方式也不逕相同,所以我也不打算談怎樣的實作才算是哪一種關係。

我們就單純討論該如何讓一個物件知道另一個物件?你在開玩笑嗎?這不是很簡單嗎?別看這這樣,Martin Fowler 可是寫了篇 《Inversion of Control Containers and the Dependency Injection pattern》文章描述幾種 dependency injection 的方法: constructor injection、setter injection 和 interface injection,以及用來查找物件的 service locator。不過既然 Martin Fowler 寫得這麼詳細了,我又有什麼好寫的呢?就三個:自己的習慣、 container-based annotated dependency injection 和 singleton,後面兩種是我覺得有意思的東西。

個人偏好

先看我自己的習慣吧!我通常將必要的物件關係使用 constructor injection,例如 A 物件需要 B 和 C 物件才能運作,便會在 constructor 宣告 B 和 C 物件的參數,在建立 A 物件時,就要傳入 B 與 C 物件,但 constructor injection 會有缺點,首先,當需要的物件關係越多,常會造成 constructor 出現 long parameter list 的 bad smell,不過這也好,提醒自己,代表著 A 物件似乎做太多事了,可能也出現 low cohesion 的問題;再者,是建立物件的先後順序會受限,甚至會出現建立 A 物件時需要物件 C,建立 C 物件可能會需要 D 物件,但建立 D 物件時需要 A 物件,此時就出現雞生蛋、蛋生雞的問題了,這時,就只能用 setter injection 解開這個迴圈了。

Setter injection 則用在預計執行期間會換掉的關係上,例如根據外部輸入的條件,更換不同的演算法,像是根據購買物品的種類或是數量,使用不同折扣計算的演算法。至於,interface injection 則只用過二次,一次是使用 plug-in 架構時,為了讓 plug-in 能取得可能需要的 host resource 物件,只要載入的 plug-in 有實作特定 interface,就替該 plug-in 注入所需的物件 (參閱閒談軟體設計:Plug-in)。至於另一次經驗,等以後有機會再提。

接著看 Container-based annotated dependency injection,有在用 Spring framework 或是使用 J2EE container 的開發者對於 @Autowired 或是 @Inject 等 annotation 應該很眼熟,自己在用 Spring framework 開發 Web service 時也用很多,像下面的程式,只要一個單純的 annotation,Spring framework 就會幫開發者注入合適的物件:

但要能讓 Spring framework 注入物件, UserManager 本身必須是個 Spring bean 物件,不論是用 @Bean 或是用 xml 讓 Spring framework 將 UserManager 建立為 bean 物件,這些關係的注入才會生效。就如同《Spring Boot in Action》書中所說的:

Like any framework, Spring does a lot for you, but it demands that you do a lot for it in return.

不過,就如前篇閒談軟體設計:友善的距離,我對框架都會保持友善的距離,因此,我很少會向上例那樣,直接將 @Autowired 這類框架專屬的 annotation 加到 domain 物件中,那該怎麼辦呢?一般來說 domain 物件我習慣放在有 -core 後綴詞修飾的 projet 中,真正提供服務或是與 Spring framework 整合的物件則放在 -ws 或 -spring 修飾的對應 project 中,例如下圖:

raw-image

UserManager 在 acl-core 的 project 中,使用 Spring framework 提供 Web Service 服務的 UserManagerBean 則是在 acl-spring 的 project 中。

雖然,UserManagerBean 沒有任何特殊的 method,只是一個單純的繼承,有點多餘的感覺,卻也多了點距離。另外,也讓測試變得簡單一點,因為測試時,不需要 Spring framework 的介入,在以前還沒有 SSD 的時候,光是等待 Spring framework 啟動然後注入物件就要十幾秒,但一個單元測試 method 可能才執行 0.x 秒,即便有 SSD 將啟動時間縮短到數秒,整體來說,這個代價實在太高了,而且當測試越多,代價就跟著提高。所以測試 domain 物件的邏輯時,雖然 annotation 很方便,我還是喜歡回歸到單純的 constructor injection 或 setter injection。

最後,就是 singleton 了,我很刻意不使用的 design pattern,或是說,我通常只有在真的只允許一個物件實體的情況下才會使用,但若看網路上 iOS 或 Android 大量的範例程式碼,singleton 被大量被當成 service locator 使用,特別是 Activity 是一個生命週期完全被 Android 掌控的物件,開發者既無法自己建立 Activity 物件,也不知道該在什麼時候使用 setter injection 注入所需要的 domain object,或是想在不同的 Activity 間傳遞複雜的物件,又或是不想一路傳遞物件到較深的物件中,於是能以 static 方式或是從全域取得物件的 singleton 就被大量使用了,但我覺得這完全不是 singleton 原本的意圖

在最近的專案中,我試著用 interface injection 的方式,搭配 Android 對 Activity 的 lifecycle callback,在有實作特定 interface 的 Activity 注入需要的物件,同樣地,Fragment 也能用這種方式注入物件,因此不需要依賴 singleton 作為 service locator。

小結

以上,就是和大家分享的三個有趣的設計決策思考。雖然,目前規劃中的主題,大概還有四篇,如果平時還有想到有趣的東西會再加入,但如果有希望我分享和討論的主題,可以留言給我,若能幫上忙,有空就分享出來。

53會員
104內容數
這是從 Medium 開始的一個專題,主要是想用輕鬆閒談的方式,分享這幾年軟體開發的心得,原本比較侷限於軟體架構,但這幾年的文章不僅限於架構,也聊不少流程相關的心得,所以趁換平台,順勢換成閒談軟體設計。
留言0
查看全部
發表第一個留言支持創作者!
Spirit的沙龍 的其他內容
這本書其實是參加 Agile Taipei 2018 時買的,還跟作者簽名合照,回到家後很『不』快地看完,大概是因為自己喜歡待在新創公司,有點難體會『大』企業的轉型困難點,現在回頭看一下當年畫的筆記,多了不少感受。
這書是在 Agile Meetup 2016年二月聚會聽完作者本人的演講才知道的,但手上的書一堆,也就一直沒有再買進,最近碰巧想試著練練鋼筆字養心,就跟著練字帖一起買回來看,因為已經聽過演講,看起來也就很快,但這次感觸卻更多,因為裡面很多內容都可以套用在自己身上,試著去分析自己的盲點,挺有意思的。
工作中,Scrum 跑的對不對,不是最重要的事了。在看這一本書時,想到的大多是 2016 在某公司推廣 Scrum 的經歷,很多是在這本書都提到了,很適合想要推廣敏捷前,先讀的一本好書。
不重新造輪子,我們使用第三方函式庫,聽起來很合理,但每個被引入的函式庫意味著一種 coupling,看到套件管理工具下載眾多第三方函式庫,意味著不用重寫這些東西,開發效率能提升數倍甚至數百倍,但我們真的都能掌握這些 coupling 嗎?當這其中任何一個環節出錯,我們的系統架構真的很優雅地應付嗎?
Offline first 的設計最近有越來越多的感覺,但好的 Offline first 設計要解決蠻多的問題,是否使用 offline first 設計真的需要好好思考,不然可能得不到好處,反而還引起一堆 bug,本篇先探討在 client 端可能會遇到的問題與一些可能的解法。
會後,我與其中一位創辦人聊聊他們 scrum 怎麼跑,以及程式、美術與企劃,這三種技能差異甚大的成員怎麼合作,他也苦笑,其實他們也花了很多時間磨合,但我們都提到,要引導團隊需要的不是 process,而是很多的軟技能,讓團隊自己能夠成長。
這本書其實是參加 Agile Taipei 2018 時買的,還跟作者簽名合照,回到家後很『不』快地看完,大概是因為自己喜歡待在新創公司,有點難體會『大』企業的轉型困難點,現在回頭看一下當年畫的筆記,多了不少感受。
這書是在 Agile Meetup 2016年二月聚會聽完作者本人的演講才知道的,但手上的書一堆,也就一直沒有再買進,最近碰巧想試著練練鋼筆字養心,就跟著練字帖一起買回來看,因為已經聽過演講,看起來也就很快,但這次感觸卻更多,因為裡面很多內容都可以套用在自己身上,試著去分析自己的盲點,挺有意思的。
工作中,Scrum 跑的對不對,不是最重要的事了。在看這一本書時,想到的大多是 2016 在某公司推廣 Scrum 的經歷,很多是在這本書都提到了,很適合想要推廣敏捷前,先讀的一本好書。
不重新造輪子,我們使用第三方函式庫,聽起來很合理,但每個被引入的函式庫意味著一種 coupling,看到套件管理工具下載眾多第三方函式庫,意味著不用重寫這些東西,開發效率能提升數倍甚至數百倍,但我們真的都能掌握這些 coupling 嗎?當這其中任何一個環節出錯,我們的系統架構真的很優雅地應付嗎?
Offline first 的設計最近有越來越多的感覺,但好的 Offline first 設計要解決蠻多的問題,是否使用 offline first 設計真的需要好好思考,不然可能得不到好處,反而還引起一堆 bug,本篇先探討在 client 端可能會遇到的問題與一些可能的解法。
會後,我與其中一位創辦人聊聊他們 scrum 怎麼跑,以及程式、美術與企劃,這三種技能差異甚大的成員怎麼合作,他也苦笑,其實他們也花了很多時間磨合,但我們都提到,要引導團隊需要的不是 process,而是很多的軟技能,讓團隊自己能夠成長。
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
這篇文章探討了在軟體開發中的技術債可能來自哪些原因,以及如何自動化偵測與修復技術債。作者透過分享不同情境下的技術債選擇,提供了對於技術債的思考與建議,針對開發人員在需要做出無奈的技術決策時,提供了一些建議。此外,還提供了一些在做出技術決策時的方法,如保留抽象層和避免vendor lock-in。
Thumbnail
今天來聊個最近很夯的主題 DDD,但不是 DDD 的本尊 Domain Driven Design,而是無所不在的 Database Driven Design,Database Driven Design 不是不好,只是你的模型容易變成貧血模型,邏輯都集中在 service 層等等。
Thumbnail
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
起源是當時 Facebook 有篇文章討論不少人分不清楚上述二者的差別,當時寫了首部曲《閒談軟體設計:API Naming Style》,接著是《閒談軟體設計:內部函式庫》,但始終沒談到 library 和 framework 的差別,主要是沒有好的例子,這次這例子還蠻不錯的。
Thumbnail
我自己偏好用 Repository 搭配 decorator 來管理 cache,而不是在 controller 層或是到處都有快取的邏輯,如果程式都是透過 Repository 更新資料,Repository 就會是一個不錯的地方更新快取,邏輯也就不會散亂在各處了。
Thumbnail
前言須知-《閒人隨筆》 看官沒錯,歡迎來到閒人的復耕系列《閒人隨筆》,沒錯本系列所有作品是閒人將以往作品重新經查證,彙整的新文章。白話說法補足故事彙整或翻譯失誤等。
Thumbnail
最近身旁有幾位正在懷孕、或剛生產完的朋友,讓我想起自己在懷孕期間印象最深刻的三件「怪事」,其中又以第三件事最誇張。
Thumbnail
不知道大家在買房之前是不是都會參考親朋好友的意見,或是上網看一些買房注意事項,有時候考慮了這塊就忘了那塊,考慮的那塊又忘了這塊.......
Thumbnail
遊戲裡,汪達憑著一把劍、一把弓、一匹馬,為愛.寧死不退。 現實中,為了生活、目標、所愛的家人,只能勇敢向前。
Thumbnail
關於片名   台灣片名《花漾女子》,原文片名《Promising Young Woman》,台灣譯名將時間定格在悲劇發生前,而原文片名則進一步帶我們看見另一個可能性結果
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
這篇文章探討了在軟體開發中的技術債可能來自哪些原因,以及如何自動化偵測與修復技術債。作者透過分享不同情境下的技術債選擇,提供了對於技術債的思考與建議,針對開發人員在需要做出無奈的技術決策時,提供了一些建議。此外,還提供了一些在做出技術決策時的方法,如保留抽象層和避免vendor lock-in。
Thumbnail
今天來聊個最近很夯的主題 DDD,但不是 DDD 的本尊 Domain Driven Design,而是無所不在的 Database Driven Design,Database Driven Design 不是不好,只是你的模型容易變成貧血模型,邏輯都集中在 service 層等等。
Thumbnail
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
起源是當時 Facebook 有篇文章討論不少人分不清楚上述二者的差別,當時寫了首部曲《閒談軟體設計:API Naming Style》,接著是《閒談軟體設計:內部函式庫》,但始終沒談到 library 和 framework 的差別,主要是沒有好的例子,這次這例子還蠻不錯的。
Thumbnail
我自己偏好用 Repository 搭配 decorator 來管理 cache,而不是在 controller 層或是到處都有快取的邏輯,如果程式都是透過 Repository 更新資料,Repository 就會是一個不錯的地方更新快取,邏輯也就不會散亂在各處了。
Thumbnail
前言須知-《閒人隨筆》 看官沒錯,歡迎來到閒人的復耕系列《閒人隨筆》,沒錯本系列所有作品是閒人將以往作品重新經查證,彙整的新文章。白話說法補足故事彙整或翻譯失誤等。
Thumbnail
最近身旁有幾位正在懷孕、或剛生產完的朋友,讓我想起自己在懷孕期間印象最深刻的三件「怪事」,其中又以第三件事最誇張。
Thumbnail
不知道大家在買房之前是不是都會參考親朋好友的意見,或是上網看一些買房注意事項,有時候考慮了這塊就忘了那塊,考慮的那塊又忘了這塊.......
Thumbnail
遊戲裡,汪達憑著一把劍、一把弓、一匹馬,為愛.寧死不退。 現實中,為了生活、目標、所愛的家人,只能勇敢向前。
Thumbnail
關於片名   台灣片名《花漾女子》,原文片名《Promising Young Woman》,台灣譯名將時間定格在悲劇發生前,而原文片名則進一步帶我們看見另一個可能性結果