l

2019年3月18日 星期一

落實TDD的三個難題(中):通用語言的建立

March 18 17:02~18:11

▲廣義的說,TDD、ATDD、BDD、SEB都是同義字。


難題二

落實TDD/ATTD/BDD/SBE的第二個難題就是通用語言的建立。傳統TDD(先寫失敗的單元測試)好像只是開發人員自己的事情,它被降級為一種開發方式的選擇(Test First VS. Code First),一種技術議題,與商業無直接關係。

但後來的TDD,也就是冠上ATTD/BDD/SBE之後的TDD,強調透過開發團隊與領域專家的合作,以「舉例」的方式一起釐清需求與商業價值

這裡說的領域專家,可能是Scrum團隊的Product Owner,或是任何具備問題領域知識的stakeholders。這是一種頻繁、高互動性且非常燒腦的活動,而不是單方面由Product Owner寫好user story然後在sprint planning meeting「宣達聖旨」給團隊的那種溝通模式,更不是瀑布式開發那種「文件丟過牆」的溝通模式。

***

通用語言

通用語言(Ubiquitous Language)是領域驅動設計(Domain-Driven Design;DDD)所提出的觀念,意指「在一個特定領域中,所有人所固定使用的術語」。例如,在貓奴這個特定領域(bounded context),大家對於乾乾、濕濕、罐罐、浪浪、小橘、賓士、虎斑、三花、玳瑁、麒麟尾、鏟屎官、聖上、皇后、離胺酸、貓砂、逗貓棒、結紮、貓毛、掃地機器人等名詞有著高度共識。一群貓奴聚在一起,用他們的「通用語言」可以非常有效率溝通而且比較不會造成誤會

這個概念,應用在軟體開發上面,當團隊建立了良好的的通用語言,問題領域的知識可以直接出現在解決方案領域,商業人士可以直接和技術人員用相同的語言溝通,表達商業邏輯。更進一步,在實作軟體系統時,這個通用語言將直接反應在程式碼裡面,也就是ubiquitous language in code。如此一來,程式將變得更加容易理解與維護,也更容易擴充。這是一種讓軟體變軟,擁抱改變的做法

***

通用語言和TDD有什麼關係?

在〈落實TDD的三個難題(上):領域模型與軟體架構〉中Teddy提到建立領域模型對於TDD的重要性,而建立通用語言的過程,同時間幫助團隊建立領域模型,兩者相輔相成。

只要Teddy聽到有朋友「宣稱」採用TDD開發軟體,Teddy一定會問對方:「你們的需求如何產生(如何撰寫失敗的驗收測試)?」如果答案是「Product Owner寫好給我們照做」,那麼落實TDD的程度就還有不少改善的空間。

如果答案是「透過頻繁地與Product Owner溝通,討論列出需求中的重要例子(key examples)」,那就比單向的接受user story要好很多。如果答案是「透過與Product Owner以及stakeholder的討論,以舉例的方式,建立共同討論的語言,並以此為基礎來撰寫程式」,那就可以獲得一張「好棒棒」貼紙。

***

結論

TDD是透過先撰寫測試案例來釐清需求或規格,之後再考慮如何開發程式的一種設計方法。套用建築師Alexander的模式框架,這是一種「先決定Context,再決定Form」的設計方法。

為什麼網路上的TDD範例與TDD Kata你都做得嚇嚇叫,公司的專案做起來卻讓人很想睡覺?很簡單,因為前者具有定義清楚的Context,而後者的Context非常模糊。

一般來講,Product Owner與stakeholders擁有比較多的領域知識,換句話說他們比較了解Context。缺少這些角色的幫忙,開發人員很難獨自透過TDD的方式自行決定Context,這也是許多人在真實專案中落實TDD所遭遇到的困難。

在許多公司,Product Owner與stakeholders覺得「撰寫失敗的測試案例是開發人員的事」,人家不願意理你啊。

難怪你的TDD又變成XD了。

***

友藏內心獨白:不能放任開發人員自由發揮,結果會很恐怖。

2019年3月15日 星期五

落實TDD的三個難題(上):領域模型與軟體架構

March 15 07:11~09:11


緣起

幾年前有一位泰迪軟體忠實學員問Teddy:「我上過TDD的課,但回公司後卻不知道該怎麼落實,為什麼?」當時Teddy無法回答這個問題,因為:(1)他上的TDD課程不是Teddy教的,不知道他學了什麼;(2)Teddy以前工作上開發的軟體,只有不到10%是採用TDD,其他大部分都是用傳統OOAD,code first(先寫production code再寫test code)的方式,所以沒在工作上遇到全面落實TDD的問題。

雖然工作上TDD用的不多,但Teddy寫了很多測試案例,也做了持續整合,算一算也有16年的時間。這幾年因為教學需要,投資大量時間在TDD/BDD/SBE以及DDD/Clean Architecture上面,直到最近才慢慢有種可以清楚回答N年前這個問題的感覺。

***

例子太小

不少人都是透過網路上的例子或是TDD Kata來學習TDD,這些例子大多具備以下特點:

  • 所需物件很少:只有單一或少量物件、例如著名的Bowling Game Kata,只需要一個Game物件就搞定。
  • 商業邏輯明確:上述提到的Bowling Game Kata,或是計算不同方式的郵寄費用(平信、掛號、國內快捷、國際郵件)、商品費用(一般客戶、VIP、大量採購、特價商品)等例子,它們要解決的問題「商業邏輯」都非常明確,很容易透過TDD,採用逐次完成每一個例子的方式來實作完整商業邏輯。
  • 不須考慮架構:因為例子小,邏輯清楚,所以也不需要考慮軟體架構的問題。

以上特性,對於一個「以學習TDD為目的」的例子來說,原本都不是問題,反而是優點。因為例子很專注在特定的小問題上面,所以學習者可以在短時間內把握TDD的精神:

  • 寫一個失敗的測試案例
  • 用「最笨」的方式撰寫程式碼讓測試案例通過
  • 重構程式

***

問題在哪裡?

當使用者學了TDD要實際應用在工作上的專案,此時卻發現,實際要解決的問題放大了N倍。不只物件變多、商業邏輯複雜,連帶著軟體架構也需要一起考慮。這些都是在學習TDD階段沒有冒出來的因素(forces)。

奇怪,為什麼看別人TDD,只要透過撰寫測試案例,物件與介面好像信手拈來就有。換成我來TDD,就D不出來,最後只能XD。


▼如下圖所示,這個問題隨著驗收測試開發(ATTD)、行為驅動開發(BDD)、實例化規格(SBE)等方法,將原本傳統TDD「先撰寫失敗單元測試」提升為「先撰寫失敗驗收測試」之後稍有緩解。驗收測試提供TDD一個更大的Context (背景、脈絡),讓開發人員擁有更多的資訊來「隔空抓藥」,透過測試案例描述物件以及物件之間的互動關係。


▼Specification By Example範例


但就算有了驗收測試,整個系統的全貌還是無法清楚呈現,透過每次撰寫失敗測試案例來完成系統依舊屬於由下而上的開發方式。

由下而上的開發方式最大的問題就是:「最後兜出來的系統很容易長歪掉。」看到這裡鄉民們可能會想:「敏捷開發是一種迭代與增量的方法,這不也是一種由下而上的方式?難道敏捷開發也很容易長歪掉嗎?

沒錯,完全正確。這也是為什麼現在敏捷開發流行採用「影響力對照(impact mapping)」與「用戶故事對照(user story mapping)」協助團隊關照系統全貌。撰寫程式之前,不管這個程式是test code還是production code,如果對於所開發的系統缺少一種「整體的感覺」,最後的系統設計就很容易長歪掉。

***

怎麼辦?

其實答案很簡單,就是準備「剛剛好的事前設計」(just enough up-front design)。問題是怎麼拿捏這個「剛剛好」?

▼如下圖所示,對照OOAD與TDD/BDD/SBE的做法,後者少了強調「建立領域模型」(domain model)這個步驟,讓鄉民以為domain model裡面的物件,不需要特別分析與設計就會自然而然隨著撰寫失敗的測試案例而冒出來。就算是剛開始找到的物件不洽當,反正最後總是可以透過重構來改善設計品質。


能力強者如Kent Beck或Uncle Bob等級的人物,在腦海中已有某種 皇輿全覽圖「軟體全貌地圖」,因此可以信手拈來得到合適的領域物件。就算設計不小心歪掉,後續採用重構來改善系統設計品質對他們而言也不是難題。大師們只需極小化的事前設計便可順利透過TDD完成系統,但一般大眾畢竟敏捷性沒有那麼高,所以適量的事前設計有助於TDD。

扯了這麼久還是沒講到具體解法。以下是Teddy建議的方向:

  • OOAD:如果鄉民們學過OOAD,可以參考80-20原則。找出系統中20%最優先的use case或user story,花一點點時間地建立domain model。有了這個domain model,對於後續將「失敗測試案例」轉成test code會很有幫助。


▼cleanKanban系統的領域模型


  • DDD:參考領域驅動設計方法(Domain-Driven Design;DDD),建立domain model與通用語言(Ubiquitous Language)。有這兩項「致命武器」,後續不管你想「怎麼D」都可以得心應手。


▼透過事件風暴(event storming)找出領域事件與建立領域模型


  • Clean Architecture:軟體架構百百種,屬於「插件式架構」的Clean Architecture,因為具備高度擴充性與可測試性,很適合作為各種軟體的「預設架構」。搭配Clean Architecture,撰寫TDD的失敗驗收測試直接對應到呼叫Use Case,而Clean Architecture的Use Case有著固定的結構,需要定義清楚的Input與Output介面。也就是說,Clean Architecture限縮了開發者的「選擇性」,而讓TDD的開發的工作變得更簡單。

▼搭配Clean Architecture採用TDD開發所撰寫的失敗驗收測試。

***

結論

敏捷開發與TDD都不鼓勵大量事前設計(Big Up-Front Design;BUFD),但並不是說不需要任何事前設計直接「帶著鋼盔往前衝」就可以攻克敵軍山頭。合適的事前設計,可以幫助開發人員釐清目標,支撐後續迭代與增量式開發活動,讓整體設計慢慢湧現。

***

友藏內心獨白:緣分到了,問題就想通了。


廣告

對於Clean Architecture搭配TDD與Event Storming(事件風暴)有興趣的鄉民,可參考泰迪軟體的【Clean Architecture這樣學就會了實作班】,2019年4月份課程已確定開課。

2019年3月14日 星期四

無用之用

March 14 16:37~18:06


The Timeless Way of Building》這本書從2003年5月30日入手起算到現在已將近16年。剛到手時幾乎每天都在傻傻地讀,因為看不懂作者想要表達的真正意思,只能硬著頭皮念下去。這本書的英文用字與文法不算太難,而且有中文版可以對照著看,所以讀不懂的主要原因倒不是因為語言隔閡,而是無法理解作者背後的整體思想。作者的腦袋裡不知道裝什麼,真是太奇葩了XD。

有人可能會問:「念資工的幹嘛去讀建築的書?當然看不懂啊。」當初因為博士論文想研究設計模式,指導教授說:「要研究pattern,不能只看GoF 的《Design Patterns》這本書,要從源頭去研究pattern發明人Alexander的作品。

就因為這句話,Teddy立刻上Amazon買了《The Timeless Way of Building》、《A Pattern Language》以及《Notes on the Synthesis of Form》各兩本。一本自己讀,另一本給指導教授,「暗示」指導教授也要一起讀。總不能只有Teddy一人受苦啊 XD。

***

印象中,過了一段瞎子摸象的日子,後來慢慢地有點感覺,可以將書中的一些講法「硬套」在軟體開發上面,有種重見光明的感覺。

數年後,一直到博士班畢業,Teddy都還沒把這本書整本看懂。又過了幾年,泰迪軟體成立後因為要賺錢生存下來,在設計【Design Patterns這樣學就會了–入門實作班】教材時,Teddy特別在第一天的課程介紹Alexander的方法。幾年下來,「Design Patterns這樣學就會了–入門實作班」教了20幾次,不斷地修改教材內容,對這本書的體會又更深了一些。

***

五年多前Teddy開始讀《Domain-Driven Design》(DDD),一開始也搞不清楚DDD到底在搞什麼。表面上看起來,就是另一種物件導向分析設計(OOAD)的方法啊,但仔細一看,和傳統的OOAD味道又不一樣。那是個有點陌生但又熟悉的味道,耶,原來DDD是一種pattern language!作者Eric Evans很顯然也受到Alexander的影響。有了這層認識,再回頭看DDD的眼光就不一樣。不但看得更深入,也可以重複使用以前讀Alexander書本的那些知識。

這就是Teddy之前說的:「有九陽神功護體,學什麼功夫都快。

***

兩個多禮拜前Teddy因為要設計【Clean Architecture實作班】課程教材,用TDD/Specification By Example(SBE)加Clean Architecture加DDD的Event Storming、Ubiquitous Language與Aggregate等技術,幾乎「無痛地」很快就把範例做好。之前對於TDD/SEB有一些沒想清楚的地方,在無形之中居然豁然開朗。這並不是因為這兩個禮拜Teddy吃了天山雪蓮突然功力大增,而是之前下的功夫點點滴滴累積,剛好在這個時間點「因緣成熟」而豐收

如果沒讀過Alexander的書,也許這一切「好事」都不會發生。

感恩seafood、讚嘆seafood。

***

友藏內心獨白:還好在台灣這本書沒什麼人讀,少了很多競爭對手。

2019年3月12日 星期二

關於時間的測試

March 12 07:51~09:07


時光一逝永不回

程式中如果有使用到「時間」,對測試來說是一個很傷腦筋的問題。例如,在看板系統中,想測試一個工作項目(work item)的lead time(從開工到交貨的時間)或cycle time(在某個或某些工作階段停留的時間),如果採用手動測試,測試人員需要不斷修改電腦時間,以便模擬出工作項目停留在不同工作階段的狀況

在〈對付時好時壞的測試案例(5):Time〉Teddy曾經談過這個問題,今天要從程式碼的角度再談一次。

***

程式範例

為了製作「Clean Architecture實作班」課程範例,Teddy最近忙著開發一個看板系統軟體稱為cleanKanban。Teddy參考領域驅動設計(Domain-Driven Design;DDD)的作法,套用Aggregate設計模式。一個aggregate將一小群物件包裝在一起,最上層的物件稱為aggregate root,負責維持不變量(invariant)與交易一致性。

不同Aggregate之間的狀態透過領域事件(domain event)保持同步,下圖AbstractDomainEvent類別代表領域事件的抽象類別,其中occurredOn屬性紀錄事件發生的日期與時間。

上圖中AbstractDomainEvent類別直接透過 new Date()獲得日期物件,這種寫法要撰寫自動測試就很傷腦筋。

學過測試替身Test Double(1):什麼是測試替身?〉的鄉民應該會想到以下兩種解法:

  • 相依性注入技巧,AbstractDomainEvent不要自己產生Date物件,而是讓呼叫它的物件傳入。這種做法有兩個潛在問題:
    • 需要改程式,破壞了既有程式的介面。如果AbstractDomainEvent已經有很多子類別實作,異動到的程式就比較多。
    • 客戶端比較難用。很多領域物件(domain object)都會產生領域事件,代表事件產生時間的occurredOn屬性如果只是因為測試的原因需要外部注入,平添客戶端的麻煩。
  • 採用mock object,透過mock object在測試時直接攔截 new Date()呼叫,注入特定日期。這種做法不需要改程式,在測試案例上動手腳即可。詳細討論可參考stackoverflow的這篇文章

***

透過第三者

除了上述兩種解法,還有一種方式就是透過第三者得到時間物件。撰寫DateProvider類別,透過它間接獲得日期物件。DateProvider 允許使用者注入一個日期物件,在測試模式下可注入特定日期達到模擬不同日期的自動化測試目的。

▼修改AbstractDominEvent的建構函數,透過DateProvide.new() 傳回日期。


▼在測試案例中,將特定日期傳給DateProvider,可以模擬領域事件發生在特定日期的情況。

***

結論

今天談到關於日期測試的三種方法,各有優缺點,視不同情況可能都會派上用場。DateProvider的缺點是,萬一有人要搞破壞在production code裡面呼叫到DateProvider.setDate(),注入一個特定日期,系統狀態可能就整個錯掉。這個問題可以透過把DateProvider寫得更「精緻」一點,只允許在測試環境被注入日期來改善。

關於更多測試技巧,歡迎參考泰迪軟體的【單元測試這樣學就會了實作班】。

***


友藏內心獨白:有月光寶盒才可以穿梭時空。

2019年3月6日 星期三

最新課程:【Clean Architecture實作班】

March 06 14:00~16:20


今年泰迪軟體新規畫一門兩天課程:【Clean Architecture實作班】(簡潔架構實作班),這是去年「Clean Architecture嘴砲班」的進化版。今年的新課程,打嘴砲練練嘴上功夫還是必要的,此外增加了一個完整的實作練習,讓大家動動腦與練練手上功夫。

關於這個練習題目Teddy規劃了超過一整年,去年設計好的題目經過實驗發現整體來說有點太難。於是今年Teddy從新設計一個練習範例,除了學習Clean Architecture以外,還同時兼顧TDD/BDD/SBE,並借用DDD(領域驅動設計)的Ubiquitous Language(通用語言)Event Storming(事件風暴)AggregateEntity來幫助學員分析domain model(領域模型)的物件邊界。

今天簡短介紹一下課程範例。

***

看板系統需求

課程範例要開發一個看板系統(Kanban System),稱為cleanKanban。看板範例如下所示,由不同工作階段所組成,以下圖為例,有待辦事項、分析、實作、測試、可佈署等五個工作階段。

▲看板範例


每個工作階段還可以分為子工作階段,以上圖為例,待辦事項細分成「想法」與「Top 6」,分析階段細分成「進行中」與「完成」。

使用者將工作項目移入不同的工作階段,以視覺化方式追蹤工作進度。每個工作階段可以設定WIP上限(Work In Progress Limit),當工作項目已達WIP上限時,就不可以再拉入工作到該工作階段。

最後,系統要計算工作項目在每個工作階段所停留的時間,稱為Cycle Time,以及Lead Time

結論就是,cleanKanban系統要支援看板系統三原則:

  • 視覺化(工作流程)
  • 限制WIP
  • 管理工作流

***

測試驅動開發/行為驅動開發

Clean Architecture的重點之一就是將系統分層,如下圖所示,由內而外分別是:Entity(Domain Model)Use CaseInterface AdapterFramework and Driver

▲Clean Architecture,圖片來源在此


課程採取測試驅動開發(TDD/BDD/SBE)方式,首先寫出規則(Specification)例子(Example)


看到這裡鄉民們可能會想:「要用BDD或Specification by Example方法,需要使用Cucumber或SpecFlow工具嗎?

答案是:可以使用但先不要用工具之前,先練習用腦,以免在學習過程中被工具綁住。本練習只需要最簡單的xUnit單元測試工具即可 。


▲用JUnit撰寫第一個失敗的驗收測試


▲因為要套用Clean Architecture,最後的驗收測試長成這樣,直接呼叫use case。

***

事件風暴(Event Storming)

Clean Architecture非常重視領域模型的建立,如果有學過物件導向分析與設計(OOAD)的朋友,可以沿用原本OOAD的技巧來建立領域模型。在這門課中,Teddy將介紹在領域驅動設計很流行的事件風暴,來協助建立領域模型與通用語言。

***

專案結構

最後開發出來的專案結構,反應Clean Architecture的三大原則:

  • 分層原則
  • 相依性原則
  • 跨層原則

***

課程費用

  • 原價$22,000元。
  • 早鳥優惠: NT$18,900/人(2019年3月19日前報名並完成繳費。)
  • 2人團報:$17,900 元/人。
  • 嘴砲班舊生優惠:$9,900 元/人(僅限五名)。

課程網址:http://teddysoft.tw/courses/clean-architecture/

上課地點:台北市(近台北車站)。2019年4月19、20日(五、六),09:30-16:30,共12小時

image

***

友藏內心獨白:一個課程三種享受—Clean Architecture、TDD與DDD。

2019年2月27日 星期三

鍛鍊你自己的九陽神功

Feb. 25 23:07~23:34


幾年前在上【單元測試這樣學就會了實作班】的時候有一位學員問Teddy…

學員:我在公司組了〈xUnit Test Patterns: Refactoring Test Code〉讀書會,但是效果不好,還是看不懂書中在講什麼。

Teddy:先撇除英文的因素,我覺得妳們看不懂是很正常的。首先,這本書在講測試,但你們很多測試的基本知識都缺乏,所以看不懂。其次,這本是是用模式(pattern)的方法來講測試,你們也不懂pattern,像是pattern的六大格式,所以讀起書來抓不到重點以及各個pattern之間的關聯性。

***

上禮拜四中午跟學生meeting,指導教授也來參加。我跟他們快速介紹event storming與DDD的aggregate和repository設計,前後大概花了10~15分鐘。指導教授當然是完全聽懂,學生「表面上」也聽懂了,但Teddy猜想實際上學生應該還不太懂,因為這些東西平常Teddy大概至少要花1~2小時來解釋。

在身邊認識的人裡面,大概也只有跟指導教授討論事情,可以享受光纖通訊的高速頻寬。大部分遇到的學生,溝通頻寬都還處在用modem撥接的速度。

***

這個時代大家做事情都在講求快速,但在讀書這件事上面Teddy動作很慢。經典好書,不是買回家之後「假裝看完」就沒事了,而是要看懂。這個過程,絕對不是看過1~2遍那麼簡單,至少看個5~6遍都是很稀鬆平常的事情。

把一個問題徹底弄清楚,有時候需要2~3年的時間的反覆迭代、沈澱、發酵。緣分到了,問題也就想通了。

知識的層次就是這樣一層、一層的累積出來。馬步扎得深,成為日後吸納新知的加速器。電影倚天屠龍記裡面張三豐說:「有九陽神功護體,學什麼功夫都快。」Teddy年輕時第一次聽到這句話,覺得哪有這種事,完全不符合邏輯,鬼扯。

老了之後才發現,這句話是真的XD。

***

友藏內心獨白:九陽神功就是你的學習力。

2019年2月26日 星期二

從領域驅動設計看Tennis Kata(下)

Feb. 25 18:20~19:38

▲人神之間的共通語言XD


Tennis Refactoring Kata介紹

鄉民們可以在這裡找到10幾個語言的Tennis Refactoring Kata程式碼。以Java語言為例,專案結構如下,一共有五個Java檔案:


  • TennisGame:定義TennisGame介面,讓TennisGame1~TennisGame3實作。


  • TennisGame1:第一個版本的重構對象,裡面有幾個很明顯的壞味道(Bad Smell),也有幾個小地方稍微把邏輯整理一下就可以完成重構任務。


  • TennisGame2:第二個版本的重構對象,這個版本的程式碼更長,用最無腦的方式把計分規則逐一展開,想要模擬不太會寫程式的新手所完成的作品。乍看之下有點頭大。但只要做過第一個版本,你已經知道網球遊戲的計分規則。只要把成對的if逐一合併之後,重構就大致完成。


  • TennisGame3:第三個版本的重構對象,這個版本的程式最精簡,只要套用rename與extract method這兩個重構就差不多可以搞定。


  • TennisTest:測試案例,因為這個kata的目的是要練習refactoring,所以範例程式提供了完整的測試案例。鄉民們在練習的時候,只要測試案例通過,就代表沒有改變程式原本的外部行為。


Teddy今天不是要討論如何重構這三個class,重構的活動就交給鄉民們自行練習。

***

和DDD有什麼關係?

▼下列程式為TennisGame2的(一種)重構結果。


鄉民們可以發現,經過重構後計算分數的邏輯分成四個階段:

  • BeforeDeuce:在deuce之前。
  • Deuce:發生Deuce的時候。
  • Advantage:差一分就獲勝的時候。
  • GameOver:這一局結束。

上述程式當然還可以進一步重構,眼尖的鄉民也許發現可以套用State設計模式,把getScore函數簡化成只有一行:

public String getScore() {
         return state.getScore();
     }

這整個過程是一小步、一小步重構之後,如果幸運的話最後走到套用State設計模式,讓自己看起來好棒棒。但是,鄉民們也可以從領域驅動(DDD)設計的角度來思考這個重構。假設鄉民們找到網球專家跟你一起討論需求,從網球專家的互動中,你們整理出以下domain model:

此時你們只關注Game與Game的計分方式。因為Game的計分方式與狀態有關,不同的狀態有不同的分數顯示邏輯。在與領域專家討論時,你們決定把Deuce之前的狀態稱為Initial,其他三個計分狀態分別為Deuce、Advantage、GameOver。

到這邊為止,有兩種可能的方式進行重構:

  • 小步快跑:繼續採用小步快跑的方式,慢慢重構系統,不要直接決定就是要套用State設計模式,除非程式的結構已經很明顯表達出這種可能性。
  • 重構為模式:與領域專家討論過後,你對於問題領域更佳清楚。domain model已經暗示了可以套用State設計模式,於是你決定「用跳的」,直接把程式重構為套用State設計模式。

以下為套用State設計模式後的檔案


套用State設計模式的TennisGame。Teddy把state切換邏輯寫在TennisGame身上,也可以改成放在每一個具體狀態類別身上。

***

結論

用最初淺的方式來看DDD,花點時間整理domain model,並建立ubiquitous language。無論採用小步快跑的重構,直接重構成設計模式,或是採用TDD的方式來開發系統,有一個共同的語言,對於任何方式的軟體開發都很有幫助

***

友藏內心獨白:寫程式有時候不是英文不好不會取名字,而是沒有花時間建立ubiquitous language。