簡體   English   中英

TDD和封裝優先級沖突

[英]TDD and encapsulation priority conflict

我剛開始在我的項目中練習TDD。 我正在開發一個項目,現在使用php / zend / mysql和phpunit / dbunit進行測試。 我對封裝和測試驅動方法的想法有點分心。 封裝背后的想法是隱藏對多個對象功能的訪問。 為了使其更清晰,私有和受保護的函數不能直接測試(除非您將創建一個公共函數來調用它)。

所以我最終將一些私有和受保護的函數轉換為公共函數,以便能夠測試它們。 我真的違反了封裝原則,讓位於微功能可測試性。 這是正確的做法嗎?

在TDD圈子中有一個非常標准的答案。 如果一個類中有功能你想要隱藏和直接測試,你應該發布一個具有該功能的類 這是TDD如何改進您的設計的一個很好的例子。

在原始類中,無關的功能已經消失,包含在發芽類中,因此原始類的設計更簡單,更好地符合單一責任原則 在發芽類中,提取的功能是它的存在理由 ,因此它適合於公開,因此它是可測試的,沒有僅測試修改。

尊重卡爾·曼納斯特的好答案,在走上卡爾建議的道路之前,你應該至少考慮一些缺點。

其中最重要的是:我們使用封裝來最小化帶有最大變化傳播概率的潛在依賴關系的數量。 在您的情況下,您在類中封裝了私有方法:它們不可用於其他類,因此不存在對它們的潛在依賴性:您對它們所做的任何更改的成本都會降至最低,並且傳播到其他類的可能性很低。類。

似乎Carl建議將一些私有方法從您的類中移動到一個新類中,並將這些方法公開(以便您可以測試它們)。 (順便說一句,為什么不在原來的課堂上公開呢?)

通過這樣做,您可以消除其他類對這些方法形成依賴關系的障礙,如果任何其他類使用它們,這可能會增加對這些方法進行查詢的成本。

你可以判斷這個未成年人的未成年人和一個有價值的價格,以便能夠測試你的私人方法,但至少要注意它。 在少數情況下,它確實是值得的,但如果您在整個代碼庫中實現這一點,那么您將大大增加這些依賴關系形成的可能性,從而將維護周期的成本增加到未知程度。

出於這些原因,我不同意Carl的觀點,他的建議是“...... TDD如何改善您的設計的一個很好的例子。”

此外,他表示,“在原始課程中,無關的功能已經消失,包含在萌芽的課程中,因此原始課程的設計更簡單,更好地符合單一責任原則。”

我認為移動的功能根本不是,“無關緊要。”而且,“更簡單”,是一個定義不明確的:當然可能是類的簡單性與其大小成反比但不是意味着最簡單的類系統將是最簡單的系統:如果是這種情況,所有類只包含一個方法,系統將擁有大量的類; 可以認為,刪除這種類內多個方法的層次結構會使系統更加復雜。

此外,單一責任原則(SRP)是眾所周知的主觀因素,完全取決於觀察者的抽象程度。 完全不是從類中刪除方法會自動改進其與SRP的一致性。 具有10種方法的Printer類具有在類的抽象級別進行打印的單一職責。 其中一個方法可能是checkPrinterConnected(),一個可能是checkPaper(); 在方法層面,這些顯然是單獨的職責,但它們並不自動建議將該類分解為更多的類。

Carl完成了,“在萌芽階層,提取的功能是它的存在理由,因此它適合公開,因此它是可測試的,沒有僅測試修改。”功能的重要性(它是存在的理由 - ness)不是公開適當性的基礎。 功能公開的適當性的基礎是最小化暴露給客戶端的接口,使得類的功能可用,同時客戶端的功能實現的獨立性最大化。 當然,如果你只是將一個方法移動到發芽類中,那么它必須是公共的。 但是,如果要移動多種方法,則必須將這些方法公之於眾,這對於客戶成功使用該類是必不可少的:這些公共方法可能遠遠不如您希望屏蔽您的某些私有方法重要。客戶。 (無論如何,我不喜歡這個,“Raison-d'etre”,因為方法的重要性也沒有明確定義。)

Carl建議的另一種方法取決於您設想的系統增長程度。 如果它將增長到少於幾千個類,那么您可能會考慮使用腳本將源代碼復制到新目錄,在該復制的源中更改所有“private”,“public”的出現,然后編寫對復制的源進行測試。 這具有復制代碼所需的時間的缺點,但保留封裝原始源的好處,但使所有方法在復制版本中可測試。

以下是我為此目的使用的腳本。

問候,

埃德柯萬

!/斌/慶典

rm -rf代碼復制

echo創建代碼復制...

mkdir代碼復制

cp -r ../www code-copy /

for i in find code-copy -name "*php" -follow ;

sed -i 's/private/public/g' $i

DONE

php run_tests.php

我剛讀了一篇關於讓模擬對象驅動你設計的好文章:

http://www.mockobjects.com/files/usingmocksandtests.pdf

當Carl說“你應該發布一個具有該功能的類”時,本文的作者將解釋你的測試如何通過使用模擬對象來指導你,如何設計你的課程以便你1)不必擔心關於無法測試私人部件,更重要的是2)這將如何改善您的設計(我將解釋卡爾斯的話)發現合作者和角色的正確責任。

作者將逐步向您介紹一個例子,使他的觀點非常明確。

這是另一篇采用相同方法的文章:

http://www.methodsandtools.com/archive/archive.php?id=90

報價:

許多以TDD開頭的人都在努力控制依賴關系。 要測試對象,請執行某些操作,然后驗證對象是否處於預期狀態。 因為OO設計側重於行為,所以通常隱藏(封裝)對象的狀態。 為了能夠驗證對象的行為是否符合預期,有時需要訪問內部狀態並引入特殊方法來公開此狀態,如getter方法或檢索內部狀態的屬性。

除了不希望對象混亂其接口並暴露其私有部分之外,我們既不想使用這些額外的getter引入不必要的依賴。 我們的測試將變得過於緊密,並且專注於實現細節。

一群來自英國的敏捷軟件開發先驅在1999年也在努力解決這個問題。他們不得不添加額外的getter方法來驗證對象的狀態。 他們的經理不喜歡所有這些破壞封裝並宣稱:我希望代碼中沒有吸氣劑! (Mackinnon等,2000&Freeman等,2004)

該團隊想出了專注於互動而非國家的想法。 他們創建了一個特殊對象來替換被測對象的協作者。 這些特殊對象包含預期方法調用的規范。 他們將這些對象稱為模擬對象,或簡稱為模擬對象。 最初的想法得到了改進,為所有常見的編程語言提供了幾個模擬對象框架:Java(jMock,EasyMock,Mockito),.NET(NMock,RhinoMocks),Python(PythonMock,Mock.py,Ruby(Mocha,RSpec)) ,C ++(mockpp,amop)。有關更多信息和鏈接,請訪問www.mockobjects.com。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM