重新设计Hamcrest

我在Hamcrest库上做了几篇文章 ,我确实很喜欢使用它,但是我希望对其进行一些更改。 我了解他们做出的大多数设计决策,但我认为其中一些确实不值得。

介绍Litecrest

我对库所做的大多数更改都有助于减轻Hamcrest的负担,因为我觉得有些事情不必要地减轻了负担。 这就是为什么我称我的更改为Litecrest。 它不会是一个实际的库; 这只是大声思考。 我也希望您能从中学到一些有关设计库的知识。

没有说明

Description接口以及StringDescriptionBaseDescription类实际上并不值得。 他们提供了列表转换为字符串好看一些不错的方法,但toString()在所有这些方法应该是足够的。 如果不是这样,可以将一些protected final方法放在BaseMatcher ,以方便地为列表构建字符串。 当然,这并没有真正遵循SRP密切,所以你可以使用类似 Description ,以提供方便的方法。

说明,否则不是很有帮助。 它的存在性假设它专门用于提供从长远来看可能不是String的输出。 作为一个使用良好的库,将其从String更改为与输出无关的类型会破坏向后兼容,但是这种更改不太可能需要。 应用YAGNIDescription类就在马桶下面。

无输出参数

所述describeTo()describeMismatch 不宜服用在Description或附加物体的任何其它类型的字符串,尤其是作为out参数(某物,以避免尽可能经常)。 由于这些方法没有返回类型开头,因此绝对没有理由使用out参数。

仔细研究问题,您将发现根本没有理由使用参数。 我了解到,他们可能一直在试图迫使匹配器的创建者不使用String串联,但事实并非如此。 如果匹配器的描述只是一个简单的小字符串,则没有理由他们不应该仅仅返回该字符串。 就个人而言,我将删除Description参数,并为它们提供String或CharSequence的返回类型。 我考虑使用CharSequence因为那样会给使用StringBuilder带来更大的动力,但是简单地返回String也不是什么大问题,因为他们可以在其上调用toString() 。 不过,我也可能会使用CharSequence ,因为我将在断言逻辑中使用StringBuilder来将输出放在一起,并且StringBuilder也可以采用CharSequence ,因此唯一的toString()必须在完成输出时被调用。

类型安全

Matcher接口采用通用参数,该参数与matches()方法一起使用,但是所述方法采用Object而不是通用类型。 javadoc声称这是由于类型擦除引起的,但我不认为这是一个问题。 我没有做任何挖掘来尝试是否可以将其切换为通用类型,但是如果我发现您实际上可以使用通用类型,则可以。 这消除了对TypeSafeMatcher的需求,因为它也检查null,因此可以用更简单的NullCheckingMatcher代替,或者只是实现它,以便断言在捕获到NullPointerException将不匹配描述更改为“为null”。 通过执行所有这些操作,我们可能会消除所有其他双倍的基类,这些基类必须加倍以覆盖类型安全匹配器和不那么重要的匹配器。 (例如: CustomMatcherCustomTypeSafeMatcherDiagnosingMatcherTypeSafeDiagnosingMatcher ,以及我加倍的ChainableMatcher ,摆脱了两个DiagnosingMatcher的影响;它们的设计很差,两次调用matches()

更改一些名字

我真的不喜欢describeTo()这个名字。 应该是describeExpected()describeMatch() 。 我了解他们遵循JMock ConstraintsSelfDescribing命名约定,但是看到他们没有费心完成其余方法签名的复制,实际上并没有任何好处。

CustomMatcher S的关系被称为OneOffMatcher S或QuickMatcher秒。 Custom是一个令人误解的名称,听起来您需要对其进行扩展才能创建自己的匹配器。

文档中的更多示例

我不确定该库中有几个类,因为它们的文档没有显示它们的使用方式,因此我不确定它们的用处。 Condition就是其中之一。 从少量的文档看来,这似乎是相对有用的,但是由于它没有提供使用示例(并且它是一个具有内部接口和两个内部类的相对复杂的文件),我不知道如何使用它。 它还没有记录其公共方法,因此我不确定它们是否需要大量研究。

FeatureMatcher已得到很好的记录,但同样没有示例。

那些为图书馆编写文档的人在任何时候都牢记这一点。 如果不是很明显(通常,即使不是很明显),则应给出使用中的类的示例。

删除无关的类

其中一些已经被直接或间接地解决了。 删除Description及其所有子类。 删除SelfDescribing ,因为它仅在Description仍然存在时才真正有用。 删除所有TypeSafe版本的基本匹配器。 卸下Diagnosing匹配器。 我不确定是否应该删除Condition因为我没有太大用。 如果保留Condition ,那么最终在核心org.hamcrest包中有五个原始的十一个类,在api org.hamcrest包中有四个原始的两个接口。

现在,让我们深入研究org.hamcrest.internal包。 ArrayIterator没什么用,因为您只能使用已经可以与foreach循环一起使用的数组。 NullSafety似乎模仿Arrays.toList()功能,但用IsNull匹配器替换了null匹配器。 我看不到这有什么帮助,因此将其删除。 ReflectiveTypeFinder可能最终会有用。 我只看到它在TypeSafeMatcherFeatureMatcher ,尽管我不确定在FeatureMatcher使用了多少。 不过,我会保留。 最后两个处理的是SelfDescribing ,我们已将其删除,因此这两个处理也是一样。 这仅使ReflectiveTypeFinder脱离了以前的五个类。

我不打算讨论所有其他匹配器。 在大多数情况下,已添加它们的用处。 由于删除了这么多的基类,几乎所有的类都可能必须进行更改。

Lambdas!

如果将新的功能范例也应用于hamcrest,则可以扩展匹配器概念的实用性。 我没有想太多,但是对于一次性匹配器,您可以修改库以包括一个新的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());
   }
}

这将使您可以编写类似于以下内容的断言:

assertThat("cats", "doesn't contain \"dogs\"", str -> !str.contains("dogs"));

实际上,我实际上已经在ez-testing迷你库中添加了LambdaAssert类,因此您可以将其与原始hamcrest库一起使用。

匹配器接口

实际上有一个Matcher接口是毫无意义的,因为hamcrest希望您扩展BaseMatcher而不是实现Matcher 。 如果您非常不想让任何人实现,那么为什么要创建一个接口? 尤其是因为BaseMatcher为我们做的唯一事情就是为describeMismatch()创建一个默认实现describeMismatch()并“实现”放置在此处的不赞成使用的方法,告诉您使用BaseMatcher而不是Matcher )。

如果您真的不希望人们使用该界面,请摆脱它。 就个人而言,由于无论如何我还是经常重写describeMismatch() ,所以我认为只需要实现接口就完全可以,而不必让JVM加载实际上为我提供任何东西的基类。

另外,由于我们现在有了Java 8,因此该接口可以仅使用默认方法来实现默认实现。 但是,我可以理解要避免这种情况,因为较旧的Java版本将无法利用这一点。

所以,要么只是BaseMatcher或没事的Matcher正在实施。

奥托罗

我还想更改其他一些小事情,例如,迫使人们重写describeMismatch()而不是提供默认值,但是我甚至不确定那个,因为默认值通常足够有效。 无论如何,即使您有一个受欢迎的图书馆,也并不意味着它是完美的。 始终注意进行重构。

不幸的是,所有这些更改都不是向后兼容的,但有时是值得的。

翻译自: https://www.javacodegeeks.com/2015/01/redesigning-hamcrest.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值