Java 8 Friday:大多数内部DSL已过时
在Data Geekery ,我們喜歡Java。 而且,由于我們真的很喜歡jOOQ的流暢的API和查詢DSL ,我們對(duì)Java 8將為我們的生態(tài)系統(tǒng)帶來什么感到非常興奮。
Java 8星期五
每個(gè)星期五,我們都會(huì)向您展示一些不錯(cuò)的教程風(fēng)格的Java 8新功能,這些功能利用了lambda表達(dá)式,擴(kuò)展方法和其他好東西。 您可以在GitHub上找到源代碼 。
大多數(shù)內(nèi)部DSL已過時(shí)
這是目前市場(chǎng)上最先進(jìn)的內(nèi)部DSL之一的供應(yīng)商的說法。 讓我解釋:
語(yǔ)言很難
學(xué)習(xí)新語(yǔ)言(或API)非常困難。 您必須了解所有的關(guān)鍵字,構(gòu)造,語(yǔ)句和表達(dá)式類型等。這對(duì)于外部DSL,內(nèi)部DSL和“常規(guī)” API都是正確的,它們本質(zhì)上是內(nèi)部DSL,但流暢度較低。
使用JUnit時(shí),人們已經(jīng)習(xí)慣使用hamcrest匹配器 。 它們有六種語(yǔ)言(Java,Python,Ruby,Objective-C,PHP,Erlang)可用,這一事實(shí)使它們成為一個(gè)不錯(cuò)的選擇。 作為特定領(lǐng)域的語(yǔ)言,他們已經(jīng)建立了易于閱讀的習(xí)慣用法,例如
assertThat(theBiscuit, equalTo(myBiscuit)); assertThat(theBiscuit, is(equalTo(myBiscuit))); assertThat(theBiscuit, is(myBiscuit));閱讀此代碼時(shí),您將立即“理解”所聲明的內(nèi)容,因?yàn)锳PI的讀法像prosa。 但是學(xué)習(xí)用此API編寫代碼更加困難。 您將必須了解:
- 所有這些方法來自哪里
- 存在哪些方法
- 誰(shuí)可能使用自定義匹配器擴(kuò)展了障礙
- 擴(kuò)展DSL時(shí)的最佳做法是什么
例如,在上面的示例中,三個(gè)之間到底有什么區(qū)別? 我什么時(shí)候應(yīng)該使用另一個(gè)? 是is()檢查對(duì)象身份嗎? equalTo()是否檢查對(duì)象是否相等?
hamcrest教程繼續(xù)著以下示例:
public void testSquareRootOfMinusOneIsNotANumber() {assertThat(Math.sqrt(-1), is(notANumber())); }您可以看到notANumber()顯然是一個(gè)自定義匹配器,在實(shí)用程序中的某個(gè)地方實(shí)現(xiàn)了:
public class IsNotANumber extends TypeSafeMatcher<Double> {@Overridepublic boolean matchesSafely(Double number) {return number.isNaN();}public void describeTo(Description description) {description.appendText("not a number");}@Factorypublic static <T> Matcher<Double> notANumber() {return new IsNotANumber();} }盡管這種DSL的創(chuàng)建非常容易,并且可能也很有趣,但是出于簡(jiǎn)單的原因,開始著手編寫和增強(qiáng)自定義DSL是很危險(xiǎn)的。 它們絕不比其通用的,功能相同的同類更好-但它們卻更難維護(hù)。 考慮一下Java 8中的上述示例:
用功能代替DSL
假設(shè)我們有一個(gè)非常簡(jiǎn)單的測(cè)試API:
static <T> void assertThat(T actual, Predicate<T> expected ) {assertThat(actual, expected, "Test failed"); }static <T> void assertThat(T actual, Predicate<T> expected, String message ) {assertThat(() -> actual, expected, message); }static <T> void assertThat(Supplier<T> actual, Predicate<T> expected ) {assertThat(actual, expected, "Test failed"); }static <T> void assertThat(Supplier<T> actual, Predicate<T> expected, String message ) {if (!expected.test(actual.get()))throw new AssertionError(message); }現(xiàn)在,將hamcrest匹配器表達(dá)式與其功能等效項(xiàng)進(jìn)行比較:
// BEFORE // --------------------------------------------- assertThat(theBiscuit, equalTo(myBiscuit)); assertThat(theBiscuit, is(equalTo(myBiscuit))); assertThat(theBiscuit, is(myBiscuit));assertThat(Math.sqrt(-1), is(notANumber()));// AFTER // --------------------------------------------- assertThat(theBiscuit, b -> b == myBiscuit); assertThat(Math.sqrt(-1), n -> Double.isNaN(n));有了lambda表達(dá)式和經(jīng)過精心設(shè)計(jì)的assertThat() API,我可以肯定,您將不再尋找用匹配器表達(dá)斷言的正確方法。
請(qǐng)注意,不幸的是,我們不能使用Double::isNaN方法引用,因?yàn)樗cPredicate<Double>不兼容。 為此,我們必須在斷言API中執(zhí)行一些原始類型的魔術(shù),例如
static void assertThat(double actual, DoublePredicate expected ) { ... }然后可以這樣使用:
assertThat(Math.sqrt(-1), Double::isNaN);好但是…
……您可能會(huì)聽到自己在說,“但是我們可以將匹配器與lambda和流結(jié)合起來”。 是的,我們當(dāng)然可以。 我現(xiàn)在已經(jīng)在jOOQ集成測(cè)試中做到了。 我想跳過所有不在系統(tǒng)屬性中提供的方言列表中的SQL方言的集成測(cè)試:
String dialectString = System.getProperty("org.jooq.test-dialects");// The string must not be "empty" assumeThat(dialectString, not(isOneOf("", null)));// And we check if the current dialect() is // contained in a comma or semi-colon separated // list of allowed dialects, trimmed and lowercased assumeThat(dialect().name().toLowerCase(),// Another matcher hereisOneOf(stream(dialectString.split("[,;]")).map(String::trim).map(String::toLowerCase).toArray(String[]::new)) );……那也很整潔,對(duì)嗎?
但是為什么我不干脆寫:
// Using Apache Commons, here assumeThat(dialectString, StringUtils::isNotEmpty); assumeThat(dialect().name().toLowerCase(),d -> stream(dialectString.split("[,;]")).map(String::trim).map(String::toLowerCase()).anyMatch(d::equals) );無需Hamcrest,只需普通的舊lambda和溪流!
現(xiàn)在,當(dāng)然,可讀性只是一個(gè)問題。 但是上面的示例清楚地表明,不再需要 Hamcrest匹配器和Hamcrest DSL。 鑒于在接下來的2-3年內(nèi),所有Java開發(fā)人員中的大多數(shù)將非常習(xí)慣于每天使用Streams API,而不是非常習(xí)慣于使用Hamcrest API,因此,我敦促JUnit維護(hù)人員不要使用使用Hamcrest以支持Java 8 API。
哈姆克雷斯特現(xiàn)在被認(rèn)為是壞人嗎?
好吧,它過去已經(jīng)達(dá)到了目的,人們對(duì)此已經(jīng)有所適應(yīng)。 但是,正如我們?cè)谏弦黄嘘P(guān)Java 8和JUnit Exception匹配的文章中已經(jīng)指出的那樣,是的,我們確實(shí)相信Java的人們?cè)谶^去的十年中一直在樹錯(cuò)誤的樹。
缺少lambda表達(dá)式已導(dǎo)致各種完全膨脹的庫(kù) ,現(xiàn)在也有些無用的庫(kù) 。 許多內(nèi)部DSL或注釋魔術(shù)師也受到影響。 不是因?yàn)樗麄儾辉俳鉀Q以前遇到的問題,而是因?yàn)樗鼈冞€沒有支持Java-8。 Hamcrest的Matcher類型不是功能接口,盡管將其轉(zhuǎn)換為一個(gè)接口很容易。 實(shí)際上,Hamcrest的CustomMatcher邏輯應(yīng)該被拉到Matcher接口中,成為默認(rèn)方法。
使用諸如AssertJ之類的替代方案,事情不會(huì)變得更好。該替代方案創(chuàng)建了一個(gè)替代DSL,現(xiàn)在它已通過lambda和Streams API變得過時(shí)了(就呼叫站點(diǎn)代碼冗長(zhǎng)而言)。
如果您堅(jiān)持使用DSL進(jìn)行測(cè)試,那么無論如何Spock可能都是一個(gè)更好的選擇。
其他例子
Hamcrest只是這種DSL的一個(gè)示例。 本文展示了如何通過使用標(biāo)準(zhǔn)的JDK 8構(gòu)造和幾個(gè)實(shí)用程序方法幾乎完全將其從堆棧中刪除,無論如何,您可能很快就會(huì)在JUnit中使用它們。
Java 8將為上個(gè)十年的DSL辯論帶來很多新的吸引力,因?yàn)镾treams API還將大大改善我們看待轉(zhuǎn)換或構(gòu)建數(shù)據(jù)的方式。 但是,當(dāng)前許多DSL尚未為Java 8做好準(zhǔn)備,并且尚未以功能性方式進(jìn)行設(shè)計(jì)。 對(duì)于難以學(xué)習(xí)的事物和概念,它們有太多的關(guān)鍵字,可以使用函數(shù)更好地建模。
該規(guī)則的一個(gè)例外是jOOQ或jRTF之類的DSL,它們以1:1的方式對(duì)實(shí)際存在的外部DSL進(jìn)行建模,繼承了所有現(xiàn)有的關(guān)鍵字和語(yǔ)法元素,從而使它們從一開始就很容易學(xué)習(xí)。
你拿什么
您對(duì)上述假設(shè)有何看法? 您最喜歡的內(nèi)部DSL是什么,由于Java 8已過時(shí),它可能在未來五年內(nèi)消失或完全轉(zhuǎn)換?
翻譯自: https://www.javacodegeeks.com/2014/06/java-8-friday-most-internal-dsls-are-outdated.html
總結(jié)
以上是生活随笔為你收集整理的Java 8 Friday:大多数内部DSL已过时的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ddos需要多少钱(ddos软件多少钱)
- 下一篇: Spring Java配置:会话超时