文章目录
- 前言
- 1. 类图概览
- 2. 源码阅读
- 2.1 抽象类 BaseMatcher
- 2.1 接口 Description
- 提炼模式:空对象模式
- 2. 接口 Description 与 SelfDescribing 配合使用
- 提炼模式 模板方法
- 后记
前言
hamcrest ,一个被多个测试框架依赖的包。听说 hamcrest 的源码质量很高,特此来学习一下。建议fork原仓库,拉到本地看代码。
- 代码仓库
hamcrest github 仓库
1. 类图概览
- 看个大概的类图。很经典的接口、抽象类、子类实现
2. 源码阅读
将源码逐一拆解,提取有借鉴意义的编码方式。
2.1 抽象类 BaseMatcher
基类提供了基础方法。
- 一个很有趣的方法名
在接口层面告诉使用者不要直接实现 Matcher 接口,而是继承 BaseMatcher
@Deprecated
void _dont_implement_Matcher___instead_extend_BaseMatcher_();
2.1 接口 Description
BaseMatcher 中的方法依赖 Description 接口。
@Override
public void describeMismatch(Object item, Description description) {}
@Override
public String toString() {
return StringDescription.toString(this);
}
看看这个接口有什么特殊的地方。
提炼模式:空对象模式
- Description 接口使用静态内部类包装一个 “空对象”,表达 “什么也不做”
public interface Description {
/**
* A description that consumes input but does nothing.
*/
static final Description NONE = new NullDescription();
}
- BaseMatcher 主要使用的 StringDescription
可以用idea的查找依赖功能,能看到 “空实现” 使用的地方
2. 接口 Description 与 SelfDescribing 配合使用
- 一个有意思的写法
@Override
public String toString() {
return StringDescription.toString(this);
}
- 发现 Matcher 父接口 SelfDescribing 的使用痕迹,用于表达 Matcher 自身的信息 (由开发者决定)
public interface Matcher<T> extends SelfDescribing {}
- 接口定义
public interface SelfDescribing {
void describeTo(Description description);
}
提炼模式 模板方法
- 声明一个自己的matcher 方法调用链如下 (省掉了与该模式无关的方法和类)
- BaseMatcher 中的模板方法就是 toString()
@Override
public String toString() {
return StringDescription.toString(this);
}
- 子类需要自定义describe逻辑 (模板方法toString会回调这个方法 )
@Override
public void describeTo(Description description) {
// EG: BigDecimalCloseTo 的声明
description.appendText("a numeric value within ")
.appendValue(delta)
.appendText(" of ")
.appendValue(value);
}
后记
笔者认为,如果存在大量的抽象类实现类,hamcrest 空对象模式可以借鉴一下。
hamcrest 的 toString 模板方法把 describe 逻辑很好的解耦到子类了,下次遇到需要追加上下文的实现可以参考这个模式(如上文的BigDecimalCloseTo )。