原创文章,转载请注明出处:https://blog.csdn.net/dadiyang/article/details/88782898
写在前面
在工作中,我们经常会遇到这样的需求——比较两个对象是否相等,如果不相等的话,取出不相等的字段。
以下这些场景都需要我们对一个对象进行比较:
- 数据比对
- 做单元测试断言对象是否相等
- 前端要求对不相等的字段进行高亮显示
这种需求其实是非常简单的,但是如何优雅地解决这一类需求呢?
通常的做法是重写对象的 equals
方法。但是重写 equals
方法有很多缺点,例如:
- 每次对象属性有变更,一定要记得再重写(放心,你一定会忘记的)
- 每个对象只能有一个
equals
方法,但是可能你会需要不同的比对规则 - 只能对比两个对象是否相等,无法具体知道哪个属性不等
- 自动生成的
equals
方法无法基于getter
方法进行比对 - 对象来自第三方依赖,无法重写
equals
方法
因此,实现一个通用的比对器可以减少很多不必要的麻烦,帮助我们很好地完成这一类的需求。
缘起
我是在做数据同步的时候有这个需求,我要将数据库的数据通过一定的规则导入到 ES 中,导入完成之后,如何比对两边的数据是否一致呢?这时候一个好用的比对器就是我非常好的帮手。
另外,我在做单元测试的时候发现,经常会需要将被测方法的返回值和期望的结果做 assertEquals
断言这时这个比对器也非常有帮助。我发现很多同事经常会遇到类似的需求。
于是,我找时间自己实现了一下。
实现
使用反射对传入的对象进行比对,提供了基于字段的比较器和基于 Getter 方法的对比器,并且充分考虑扩展性,使用者可以重写字段的比对规则。功能相对简单,代码实现也不难,而且做了很多注释,具体实现可以直接查看源码。
项目地址:https://github.com/dadiyang/equator
UML图:
使用方法
因为已经上传到了 maven 仓库中,我们使用非常方便:
- 添加 maven 依赖
<dependency>
<groupId>com.github.dadiyang</groupId>
<artifactId>equator</artifactId>
<version>1.0.3</version>
</dependency>
- 初始化并调用方法
Equator equator = new GetterBaseEquator();
User user1 = new User(...);
User user2 = new User(...);
// 判断属性是否完全相等
equator.isEquals(user1, user2);
// 获取不同的属性
List<FieldInfo> diff = equator.getDiffFields(user1, user2);
扩展
我们可以通过继承并重写 isFieldEquals
方法自定义比对规则,例如我们在做单元测试的时候,对于 Date 类型的字段的比对,通常数据库不保存毫秒数,而我们 new 出来的 Date 对象则包含了毫秒数,因此我们在对包含 Date 类型字段的对象做比对的时候需要忽略日期的毫秒数。这时就可以通过重写isFieldEquals
方法来自定义了:
/**
* 日期在数据库不保存毫秒数,因此需要特殊处理,比对时间时,忽略毫秒数
*
* @author dadiyang
* @date 2019/3/23
*/
public class MmInsensitiveEquator extends GetterBaseEquator {
@Override
protected boolean isFieldEquals(FieldInfo fieldInfo) {
if (fieldInfo.getFirstVal() instanceof Date) {
Date first = (Date) fieldInfo.getFirstVal();
Date second = (Date) fieldInfo.getSecondVal();
if (Objects.equals(first, second)) {
return true;
}
// 忽略毫秒数
return Objects.equals(Math.round(first.getTime() / 1000), Math.round(second.getTime() / 1000));
}
return super.isFieldEquals(fieldInfo);
}
}
后记
对象比对是一个非常小的需求,通常我们只会写一个工具类来完成。但是写一个工具类在各个项目间随处拷贝,非常不优雅,给整个团队带来很多不必要的维护成本。而且扩展性比较差,有任何差异就需要写很多代码去实现。
这时,如果我们从具体解决某一个需求的视角上升到解决一类需求,那么就能想出更加通用和优雅的解决方案了。一个个具体的需求是无穷无尽的,以有限的人生去解决无限的需求,殆矣;但是将它们归类之后,我们会发现,需求的种类是有限的。
因此解决一类一类问题,能让我们摆脱无穷无尽的重复劳动,少加点班,多点时间陪陪家人哦。