重新设计Hamcrest
我在Hamcrest庫(kù)上做了幾篇文章 ,我確實(shí)很喜歡使用它,但是我希望對(duì)其進(jìn)行一些更改。 我了解他們做出的大多數(shù)設(shè)計(jì)決策,但我認(rèn)為其中一些確實(shí)不值得。
介紹Litecrest
我對(duì)庫(kù)所做的大多數(shù)更改都有助于減輕Hamcrest的負(fù)擔(dān),因?yàn)槲矣X(jué)得有些事情不必要地減輕了負(fù)擔(dān)。 這就是為什么我稱(chēng)我的更改為L(zhǎng)itecrest。 它不會(huì)是一個(gè)實(shí)際的庫(kù); 這只是大聲思考。 我也希望您能從中學(xué)到一些有關(guān)設(shè)計(jì)庫(kù)的知識(shí)。
沒(méi)有說(shuō)明
Description接口以及StringDescription和BaseDescription類(lèi)實(shí)際上并不值得。 他們提供了列表轉(zhuǎn)換為字符串好看一些不錯(cuò)的方法,但toString()在所有這些方法應(yīng)該是足夠的。 如果不是這樣,可以將一些protected final方法放在BaseMatcher ,以方便地為列表構(gòu)建字符串。 當(dāng)然,這并沒(méi)有真正遵循SRP密切,所以你可以使用類(lèi)似 Description ,以提供方便的方法。
說(shuō)明,否則不是很有幫助。 它的存在性假設(shè)它專(zhuān)門(mén)用于提供從長(zhǎng)遠(yuǎn)來(lái)看可能不是String的輸出。 作為一個(gè)使用良好的庫(kù),將其從String更改為與輸出無(wú)關(guān)的類(lèi)型會(huì)破壞向后兼容,但是這種更改不太可能需要。 應(yīng)用YAGNI , Description類(lèi)就在馬桶下面。
無(wú)輸出參數(shù)
所述describeTo()和describeMismatch 不宜服用在Description或附加物體的任何其它類(lèi)型的字符串,尤其是作為out參數(shù)(某物,以避免盡可能經(jīng)常)。 由于這些方法沒(méi)有返回類(lèi)型開(kāi)頭,因此絕對(duì)沒(méi)有理由使用out參數(shù)。
仔細(xì)研究問(wèn)題,您將發(fā)現(xiàn)根本沒(méi)有理由使用參數(shù)。 我了解到,他們可能一直在試圖迫使匹配器的創(chuàng)建者不使用String串聯(lián),但事實(shí)并非如此。 如果匹配器的描述只是一個(gè)簡(jiǎn)單的小字符串,則沒(méi)有理由他們不應(yīng)該僅僅返回該字符串。 就個(gè)人而言,我將刪除Description參數(shù),并為它們提供String或CharSequence的返回類(lèi)型。 我考慮使用CharSequence因?yàn)槟菢訒?huì)給使用StringBuilder帶來(lái)更大的動(dòng)力,但是簡(jiǎn)單地返回String也不是什么大問(wèn)題,因?yàn)樗麄兛梢栽谄渖险{(diào)用toString() 。 不過(guò),我也可能會(huì)使用CharSequence ,因?yàn)槲覍⒃跀嘌赃壿嬛惺褂肧tringBuilder來(lái)將輸出放在一起,并且StringBuilder也可以采用CharSequence ,因此唯一的toString()必須在完成輸出時(shí)被調(diào)用。
類(lèi)型安全
Matcher接口采用通用參數(shù),該參數(shù)與matches()方法一起使用,但是所述方法采用Object而不是通用類(lèi)型。 javadoc聲稱(chēng)這是由于類(lèi)型擦除引起的,但我不認(rèn)為這是一個(gè)問(wèn)題。 我沒(méi)有做任何挖掘來(lái)嘗試是否可以將其切換為通用類(lèi)型,但是如果我發(fā)現(xiàn)您實(shí)際上可以使用通用類(lèi)型,則可以。 這消除了對(duì)TypeSafeMatcher的需求,因?yàn)樗矙z查null,因此可以用更簡(jiǎn)單的NullCheckingMatcher代替,或者只是實(shí)現(xiàn)它,以便斷言在捕獲到NullPointerException將不匹配描述更改為“為null”。 通過(guò)執(zhí)行所有這些操作,我們可能會(huì)消除所有其他雙倍的基類(lèi),這些基類(lèi)必須加倍以覆蓋類(lèi)型安全匹配器和不那么重要的匹配器。 (例如: CustomMatcher和CustomTypeSafeMatcher , DiagnosingMatcher和TypeSafeDiagnosingMatcher ,以及我加倍的ChainableMatcher ,擺脫了兩個(gè)DiagnosingMatcher的影響;它們的設(shè)計(jì)很差,兩次調(diào)用matches() )
更改一些名字
我真的不喜歡describeTo()這個(gè)名字。 應(yīng)該是describeExpected()或describeMatch() 。 我了解他們遵循JMock Constraints的SelfDescribing命名約定,但是看到他們沒(méi)有費(fèi)心完成其余方法簽名的復(fù)制,實(shí)際上并沒(méi)有任何好處。
CustomMatcher S的關(guān)系被稱(chēng)為OneOffMatcher S或QuickMatcher秒。 Custom是一個(gè)令人誤解的名稱(chēng),聽(tīng)起來(lái)您需要對(duì)其進(jìn)行擴(kuò)展才能創(chuàng)建自己的匹配器。
文檔中的更多示例
我不確定該庫(kù)中有幾個(gè)類(lèi),因?yàn)樗鼈兊奈臋n沒(méi)有顯示它們的使用方式,因此我不確定它們的用處。 Condition就是其中之一。 從少量的文檔看來(lái),這似乎是相對(duì)有用的,但是由于它沒(méi)有提供使用示例(并且它是一個(gè)具有內(nèi)部接口和兩個(gè)內(nèi)部類(lèi)的相對(duì)復(fù)雜的文件),我不知道如何使用它。 它還沒(méi)有記錄其公共方法,因此我不確定它們是否需要大量研究。
FeatureMatcher已得到很好的記錄,但同樣沒(méi)有示例。
那些為圖書(shū)館編寫(xiě)文檔的人在任何時(shí)候都牢記這一點(diǎn)。 如果不是很明顯(通常,即使不是很明顯),則應(yīng)給出使用中的類(lèi)的示例。
刪除無(wú)關(guān)的類(lèi)
其中一些已經(jīng)被直接或間接地解決了。 刪除Description及其所有子類(lèi)。 刪除SelfDescribing ,因?yàn)樗鼉H在Description仍然存在時(shí)才真正有用。 刪除所有TypeSafe版本的基本匹配器。 卸下Diagnosing匹配器。 我不確定是否應(yīng)該刪除Condition因?yàn)槲覜](méi)有太大用。 如果保留Condition ,那么最終在核心org.hamcrest包中有五個(gè)原始的十一個(gè)類(lèi),在api org.hamcrest包中有四個(gè)原始的兩個(gè)接口。
現(xiàn)在,讓我們深入研究org.hamcrest.internal包。 ArrayIterator沒(méi)什么用,因?yàn)槟荒苁褂靡呀?jīng)可以與foreach循環(huán)一起使用的數(shù)組。 NullSafety似乎模仿Arrays.toList()功能,但用IsNull匹配器替換了null匹配器。 我看不到這有什么幫助,因此將其刪除。 ReflectiveTypeFinder可能最終會(huì)有用。 我只看到它在TypeSafeMatcher和FeatureMatcher ,盡管我不確定在FeatureMatcher使用了多少。 不過(guò),我會(huì)保留。 最后兩個(gè)處理的是SelfDescribing ,我們已將其刪除,因此這兩個(gè)處理也是一樣。 這僅使ReflectiveTypeFinder脫離了以前的五個(gè)類(lèi)。
我不打算討論所有其他匹配器。 在大多數(shù)情況下,已添加它們的用處。 由于刪除了這么多的基類(lèi),幾乎所有的類(lèi)都可能必須進(jìn)行更改。
Lambdas!
如果將新的功能范例也應(yīng)用于hamcrest,則可以擴(kuò)展匹配器概念的實(shí)用性。 我沒(méi)有想太多,但是對(duì)于一次性匹配器,您可以修改庫(kù)以包括一個(gè)新的assertThat()方法,如下所示:
public static void assertThat(T item, String description, Predicate matcher) {if(!matcher.test(item)) {StringBuilder output = new StringBuilder();output.append("Expected: ").append(description).append("\n but: was").append(item.toString());throw new AssertionError(output.toString());} }這將使您可以編寫(xiě)類(lèi)似于以下內(nèi)容的斷言:
assertThat("cats", "doesn't contain \"dogs\"", str -> !str.contains("dogs"));實(shí)際上,我實(shí)際上已經(jīng)在ez-testing迷你庫(kù)中添加了LambdaAssert類(lèi),因此您可以將其與原始hamcrest庫(kù)一起使用。
匹配器接口
實(shí)際上有一個(gè)Matcher接口是毫無(wú)意義的,因?yàn)閔amcrest希望您擴(kuò)展BaseMatcher而不是實(shí)現(xiàn)Matcher 。 如果您非常不想讓任何人實(shí)現(xiàn),那么為什么要?jiǎng)?chuàng)建一個(gè)接口? 尤其是因?yàn)锽aseMatcher為我們做的唯一事情就是為describeMismatch()創(chuàng)建一個(gè)默認(rèn)實(shí)現(xiàn)describeMismatch()并“實(shí)現(xiàn)”放置在此處的不贊成使用的方法,告訴您使用BaseMatcher而不是Matcher )。
如果您真的不希望人們使用該界面,請(qǐng)擺脫它。 就個(gè)人而言,由于無(wú)論如何我還是經(jīng)常重寫(xiě)describeMismatch() ,所以我認(rèn)為只需要實(shí)現(xiàn)接口就完全可以,而不必讓JVM加載實(shí)際上為我提供任何東西的基類(lèi)。
另外,由于我們現(xiàn)在有了Java 8,因此該接口可以僅使用默認(rèn)方法來(lái)實(shí)現(xiàn)默認(rèn)實(shí)現(xiàn)。 但是,我可以理解要避免這種情況,因?yàn)檩^舊的Java版本將無(wú)法利用這一點(diǎn)。
所以,要么只是做BaseMatcher或沒(méi)事的Matcher正在實(shí)施。
奧托羅
我還想更改其他一些小事情,例如,迫使人們重寫(xiě)describeMismatch()而不是提供默認(rèn)值,但是我甚至不確定那個(gè),因?yàn)槟J(rèn)值通常足夠有效。 無(wú)論如何,即使您有一個(gè)受歡迎的圖書(shū)館,也并不意味著它是完美的。 始終注意進(jìn)行重構(gòu)。
不幸的是,所有這些更改都不是向后兼容的,但有時(shí)是值得的。
翻譯自: https://www.javacodegeeks.com/2015/01/redesigning-hamcrest.html
總結(jié)
以上是生活随笔為你收集整理的重新设计Hamcrest的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 破门而入行动小队下载安卓汉化破解(安卓汉
- 下一篇: CDI 2.0更新