天天看點

TDD真的可以帶來良好的設計嗎?

最近我發推文說, 如果我們不知道好的設計是什麼樣的話 , TDD就無法産生好的設計 。 我還說的是,我們可能應該在TDD之前(或至少同時)教設計。 此推文導緻與JB Rainsberger , Ron Jeffries和其他一些人進行了讨論。 後來,我和JB最終在環聊直播中進行了現場讨論 。

TDD真的可以帶來良好的設計嗎?

如果您回顧我的許多演講,部落格甚至我的書,您會發現我多次說TDD是一種設計工具。 那麼,什麼改變了? 為什麼我不再說相同的話了?

我為什麼改變主意?

在更加關注我的工作方式以及其他多少開發人員的工作之後,我意識到沒有多少人通過TDD推動良好的設計。 盡管我喜歡“ RED-GREEN-REFACTORING”節奏,但“重構”步驟不足以将TDD稱為設計工具。

TDD并未規定您應該如何設計。 它的作用是不斷惹惱您,問“您确定嗎? 夠好嗎? 你能做得更好嗎?” 這種煩惱(或者不斷提醒您看一下您的設計和事情,如果可以改進的話)是一件好事,但還不夠。

在我看來,TDD是一種軟體開發工作流程 ,可為我帶來很多好處,包括不斷提醒我使代碼更好。 使我的代碼更好的含義不是TDD的一部分。

您是否忘了簡單設計的4條規則?

啊,是的...但是沒有。 我不會忘記他們。 4個簡單設計規則不是TDD的一部分,我在這裡純粹是在讨論TDD。 4簡單設計規則通常是許多經驗豐富的TDD從業人員在重構階段使用的設計準則(包括我自己,以及其他技術)。

4簡單設計規則是我們掌握的衆多設計準則之一。 SOLID是另一個。 域驅動設計是另一種。 許多其他設計原則和模式也可以作為良好指導。 這些就是我們在“重構”階段需要記住的事情。 或者,換句話說,對現有設計準則有充分的了解才能使您獲得更好的設計。

TDD是一種工作流(不是設計工具),在重構階段,您可以将現有的軟體設計知識與可以幫助您進行更好設計的設計技術相結合。

并非所有TDD都相同

TDD有兩種主要樣式,它們之間存在明顯差異,主要是在設計方面。

古典主義者

古典主義方法是肯特·貝克(Kent Beck)建立的TDD原始方法。 它也被稱為TDD的底特律學校 。

主要特征

  • 設計發生在重構階段。
  • 通常,測試是基于狀态的測試。
  • 在重構階段,被測單元可能會成長為多個類别。
  • 除非使用外部系統隔離,否則很少使用模拟。
  • 沒有進行前期設計考慮。 設計完全來自代碼。
  • 這是避免過度設計的好方法。
  • 由于基于狀态的測試且無需預先設計,是以更易于了解和采用。
  • 通常與簡單設計的4條規則結合使用。
  • 當我們知道輸入和期望的輸出是什麼,但我們真的不知道實作的樣子時,對探索很有幫助。
  • 非常适合我們不能依賴領域專家或領域語言(資料轉換,算法等)的情況

問題

  • 公開狀态僅用于測試目的。
  • 與“從外而内”的方法相比,重構階段通常更大(以下更多内容)。
  • 當在重構階段出現類時,被測試的單元将大于類。 當我們單獨檢視測試時,這很好,但是随着類的出現,它們建立了自己的生命,并被應用程式的其他部分重用。 随着這些其他類的發展,它們可能會破壞完全不相關的測試,因為這些測試使用其實際實作而不是模拟。
  • 缺乏經驗的從業人員通常會跳過重構(設計改進)步驟,進而導緻一個循環看起來更像是紅色-綠色-紅色-綠色-…-紅色-綠色大規模的重構。
  • 由于其探索性質,一些測試中的類是根據“我認為我将需要使用帶有此接口的類(公共方法)”建立的,這使得它們在與系統的其餘部分連接配接時不合适。
  • 可能會很慢而且很浪費,因為很多時候我們已經知道在被測類中我們不能承擔太多責任。 古典主義者的建議是等待重構階段修複設計,僅依靠具體證據來提取其他類。 盡管這對新手有好處,但對于經驗豐富的開發人員而言,這純屬浪費。

由外而内

從外而内的TDD,也稱為倫敦學校或嘲笑者 ,是由倫敦最早的一些XP從業者開發并采用的TDD風格。 後來啟發了BDD的建立。

主要特征

  • 與古典主義者不同,Outside-In TDD規定了開始測試代碼的方向:從外部(接收外部請求的第一類)到内部(将包含滿足該功能的單個行為的類)實施)。
  • 我們通常從驗收測試開始,該測試會驗證該功能作為一個整體是否有效。 驗收測試還可以作為實施指南。
  • 通過失敗的驗收測試,告知功能尚未完成的原因(沒有傳回資料,沒有消息發送到隊列,沒有資料存儲在資料庫中,等等),我們開始編寫單元測試。 要測試的第一個類是處理外部請求的類(控制器,隊列偵聽器,事件處理程式,元件的入口點等)。
  • 衆所周知,我們不會在單個類中建構整個應用程式,是以我們對要測試的類需要哪種類型的協作者做出一些假設。 然後,我們編寫測試來驗證被測類及其協作者之間的協作。
  • 根據被測試類的公共方法被調用時需要做的所有事情來識别協作者。 協作者的名稱和方法應來自領域語言(名詞和動詞)。
  • 一旦測試了一個類,我們将選擇第一個協作者(它是沒有實作的建立),并按照上一類所使用的相同方法來測試其行為。 這就是為什麼要從外而内的原因:我們從更接近系統輸入(外部)的類開始,并随着更多協作者的出現而向應用程式内部移動。
  • 在編寫測試時,設計從紅色階段開始。
  • 測試是關于協作和行為的,而不是狀态。
  • 在重構階段改進設計。
  • 始終建立每個協作者及其公共方法來服務現有的用戶端類,進而使代碼讀起來非常好。
  • 與經典方法相比,重構階段要小得多。
  • 促進更好的封裝,因為沒有任何狀态僅出于測試目的而公開,
  • 更明确地告訴,不要問方法。
  • 更符合面向對象程式設計的原始思想:測試是關于對象向其他對象發送消息而不是檢查其狀态的。
  • 适用于可以從使用者故事和接受條件中提取名稱和動詞的商業應用。

問題

  • 新手很難采用,因為需要更高水準的設計技能。
  • 開發人員無法從代碼中獲得回報以建立協作者。 他們在編寫測試時需要可視化協作者。
  • 由于建立過早的類型(合作者),可能導緻過度設計。
  • 不适合使用者故事中未指定的探索性工作或行為(資料轉換,算法等)。
  • 不良的設計技巧可能會導緻模拟爆炸。
  • 行為測試比狀态測試更難編寫。
  • 編寫測試時,需要具備領域驅動設計和其他設計技術的知識,包括4條簡單設計規則。

我們應該使用哪種TDD樣式?

都。 所有。 它們隻是工具,是以,應根據您的需要使用它們。 經驗豐富的TDD從業人員可以從一種樣式過渡到另一種樣式,而不必擔心他們使用的是哪種樣式。

宏觀和微觀設計

有兩種設計類型:宏觀設計和微觀設計。 微型設計是我們在測試駕駛代碼時所做的工作,主要使用經典方法。 宏設計超出了我們正在實作的功能。 這是關于我們如何在更高層次上對域進行模組化,如何拆分應用程式,層,服務等的内容。宏設計可以幫助我們對應用程式進行整體組織,并為團隊和開發人員提供了并行工作而又無需踩踏的方式彼此的腳趾。 宏設計是指企業如何看待應用程式,并且通常使用諸如域驅動設計之類的技術。 宏設計還有助于確定整個應用程式的一緻性。 TDD不會幫助您進行宏設計。

使用Outside-In TDD時通常會考慮宏設計,但僅靠Outside-In不足以定義應用程式的宏設計。

結論

多年來,我已經看到許多應用程式已經過測試驅動,但仍然很難使用。 好的,我承認它們比大多數傳統應用程式要好得多,這些應用程式之前沒有必須維護的測試。

無論開發人員是否編寫測試,任何開發人員都可能一團糟。 開發人員還可以測試驅動器廢品,無論他們使用哪種TDD樣式。

TDD 不是設計工具。 這是一個軟體開發工作流程 ,可在其生命周期中提示改進代碼。 在這些提示(編寫測試和重構)中,開發人員需要了解一些設計準則(4條簡單設計規則,域驅動設計,SOLID,模式,Demeter法則,告訴,不要問,POLA / S,按合同設計) ,功能嫉妒,内聚,耦合,平衡抽象原理等),以使其代碼更好。 僅僅說重構還不足以将TDD稱為設計工具。

許多開發人員指責TDD和模拟程式會降低速度。 他們最終放棄了TDD,因為他們努力獲得自己想要的結果。 在我看來,沒有開發人員真的很努力地了解RED-GREEN-REFACTOR的生命周期。 他們所苦惱的是如何精心設計軟體。

TDD的優點是不斷詢問我們“嘿,您能使您的代碼更好嗎? 看看這門課變得越來越困難嗎? 好的,您成功了。 這是您的綠色酒吧。 現在做得更好。” 除此之外,您是一個人。

當我們了解好的設計是什麼樣子時,TDD變得容易得多。 實踐和了解可用的大量設計指南将使TDD變得更加容易和有用。 它還将減少其學習曲線,并希望增加其采用率。

極端是壞的。 我們正在從BDUF(前期大設計)轉變為根本沒有設計 。 抛棄我們的設計知識是一個錯誤。 當然,我們不應該回到黑暗時代并對所有事物進行過度設計,而認為我們應該隻專注于微設計也是一個錯誤。 如果您是一個人工作,做幾篇文章或在一個小型應用程式上工作,那麼可以,随您便。 但是,如果您是更大的團隊的一員,開發的東西遠大于kata,那麼,如果您更加關注宏設計和代碼結構,将會對您的團隊有所幫助。

翻譯自: https://www.javacodegeeks.com/2015/05/does-tdd-really-lead-to-good-design.html