假设您有两个相同 bean 类型的实例,并且您希望显示两个实例之间发生变化的摘要 - 例如,您有一个 bean 代表应用程序中的用户设置,并且您希望能够显示用户提交的新设置(实例#1)与已经为用户存储的设置(实例#2)的更改列表。
对于这样的任务,是否有一种常用的算法或设计模式,也许可以抽象出来并重新用于不同类型的 bean?(我很难为这类问题想一个好名字来知道谷歌要做什么)。我检查了 commons-beanutils 并没有弹出任何内容。
如果您在谈论比较值,我会考虑使用反射并逐个字段地比较它们。
像这样的东西:
Field[] oldFields = oldInstance.class.getDeclaredFields();
Field[] newFields = newInstance.class.getDeclaredFields();
StringBuilder changes = new StringBuilder();
Arrays.sort(oldFields);
Arrays.sort(newFields);
int i = 0;
for(Field f : oldFields)
{
if(!f.equals(newFields[i]))
{
changes.append(f.getName()).append(" has changed.\n");
}
i++;
}
此代码尚未经过测试。您可能需要获取字段中的值并比较它们,而不是仅仅将字段相互比较,但它应该在理论上有效。
反射不会在下一次调用中保持字段的顺序:它对数组进行更安全的排序。
/*
*declarations of variables
*/
Arrays.sort(oldFields);//natural order - choice 1
Arrays.sort(newFields, new Ordinator());//custom Comparator - choice 2
/*
*logic of comparations between elements
*/
在选项 2 中,您可以使用扩展 Comparator的内部类 Ordinator 来决定排序逻辑(如何对元素进行排序)。
PS 代码是草稿
我们用 bean utils 做了一些类似的事情,而且效果很好。需要考虑的事项:您是否深入了解字段对象 - 如果一个人包含地址并且地址更改,您是说地址更改还是 address.postalCode 更改(我们这样做)?您是否从 diff 中返回列表属性名称、旧值、新值(我们这样做)?您想如何处理日期 - 如果您只关心日期部分,那么您的比较应该忽略时间?你怎么说忽略哪些字段?
这并不是真正的复制和粘贴答案,而是更多在我们编写不同之处时并不立即显而易见的事情列表。
至于实现,我们只有一个静态 util 方法,它接受两个 bean 和一个要比较的属性列表,然后将属性映射返回到包含旧值和新值的 Pair。然后每个bean都有一个diff(Object o)
方法,根据需要调用静态util方法。
这些库应该会有所帮助。
https://code.google.com/p/beandiff/ - 基于注释的 bean 差异库。阿帕奇许可证 2.0
https://github.com/SQiShER/java-object-diff/ - bean 因访问者模式而异。阿帕奇许可证 2.0
我们需要生成 json 格式的 bean 之间的差异以用于审计目的。我们最终使用beandiff库来实现它。
** 编辑 ** 这看起来像是一个较新的选项。我还没有使用它。
希望能帮助到你。
楼上的好答案。
如果您的数据在结构上发生变化,即整个字段集合可能相关或不依赖于其他字段,您可能需要考虑差异执行。
基本上,您对字段有一个循环,并且在反序列化先前值的同时序列化当前字段值,并在进行时比较它们。
如果有条件测试使字段块相关或不相关,则序列化/反序列化条件测试的真或假值,并使用它来决定是否序列化和/或反序列化受影响的字段。它很好地重现。
只是一个建议。
使用反射和标准数据结构的解决方案。
Field[] declaredFields = ClassOne.class.getDeclaredFields();
Field[] declaredFields2 = ClassTwo.class.getDeclaredFields();
ArrayList<String> one = new ArrayList<String>();
ArrayList<String> two = new ArrayList<String>();
for (Field field : declaredFields)
{
one.add(field.getName());
}
for (Field field : declaredFields2)
{
two.add(field.getName());
}
List<String> preone = (List<String>)one.clone();
one.removeAll(two);
two.removeAll(preone);
Collections.sort(one);
Collections.sort(two);
System.out.println("fields only in One : " + one);
System.out.println("fields only in Two : " + two);