10

假设您有两个相同 bean 类型的实例,并且您希望显示两个实例之间发生变化的摘要 - 例如,您有一个 bean 代表应用程序中的用户设置,并且您希望能够显示用户提交的新设置(实例#1)与已经为用户存储的设置(实例#2)的更改列表。

对于这样的任务,是否有一种常用的算法或设计模式,也许可以抽象出来并重新用于不同类型的 bean?(我很难为这类问题想一个好名字来知道谷歌要做什么)。我检查了 commons-beanutils 并没有弹出任何内容。

4

6 回答 6

6

如果您在谈论比较值,我会考虑使用反射并逐个字段地比较它们。

像这样的东西:


    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++;
    }

此代码尚未经过测试。您可能需要获取字段中的值并比较它们,而不是仅仅将字段相互比较,但它应该在理论上有效。

于 2009-02-23T16:31:37.167 回答
3

反射不会在下一次调用中保持字段的顺序:它对数组进行更安全的排序。

/*
*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 代码是草稿

于 2009-02-23T16:47:50.000 回答
3

我们用 bean utils 做了一些类似的事情,而且效果很好。需要考虑的事项:您是否深入了解字段对象 - 如果一个人包含地址并且地址更改,您是说地址更改还是 address.postalCode 更改(我们这样做)?您是否从 diff 中返回列表属性名称、旧值、新值(我们这样做)?您想如何处理日期 - 如果您只关心日期部分,那么您的比较应该忽略时间?你怎么说忽略哪些字段?

这并不是真正的复制和粘贴答案,而是更多在我们编写不同之处时并不立即显而易见的事情列表。

至于实现,我们只有一个静态 util 方法,它接受两个 bean 和一个要比较的属性列表,然后将属性映射返回到包含旧值和新值的 Pair。然后每个bean都有一个diff(Object o)方法,根据需要调用静态util方法。

于 2009-02-23T18:04:42.967 回答
3

这些库应该会有所帮助。

https://code.google.com/p/beandiff/ - 基于注释的 bean 差异库。阿帕奇许可证 2.0

https://github.com/SQiShER/java-object-diff/ - bean 因访问者模式而异。阿帕奇许可证 2.0

我们需要生成 json 格式的 bean 之间的差异以用于审计目的。我们最终使用beandiff库来实现它。

** 编辑 ** 这看起来像是一个较新的选项。我还没有使用它。

http://beandiff.org/

希望能帮助到你。

于 2012-06-15T05:23:03.050 回答
1

楼上的好答案。

如果您的数据在结构上发生变化,即整个字段集合可能相关或不依赖于其他字段,您可能需要考虑差异执行

基本上,您对字段有一个循环,并且在反序列化先前值的同时序列化当前字段值,并在进行时比较它们。

如果有条件测试使字段块相关或不相关,则序列化/反序列化条件测试的真或假值,并使用它来决定是否序列化和/或反序列化受影响的字段。它很好地重现。

只是一个建议。

于 2009-02-23T18:45:09.047 回答
0

使用反射和标准数据结构的解决方案。

    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);
于 2013-03-08T00:23:58.350 回答