Общий алгоритм для создания разницы полей в двух bean-компонентах?

Допустим, у вас есть два экземпляра одного и того же типа bean-компонента, и вы хотите отобразить сводку того, что изменилось между двумя экземплярами - например, у вас есть bean-компонент, представляющий настройки пользователя в вашем приложении, и вы хотите чтобы иметь возможность отображать список того, что изменилось в новых настройках, отправленных пользователем (экземпляр №1), по сравнению с тем, что уже сохранено для пользователя (экземпляр №2).

Есть ли обычно используемый алгоритм или шаблон проектирования для такой задачи, возможно, что-то, что можно абстрагировать и повторно использовать для разных типов bean-компонентов? (Мне трудно придумать хорошее название для такого типа проблемы, чтобы знать, на что гуглить). Я проверил commons-beanutils, и у меня ничего не выскочило.

Ответов (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++;
    }

Этот код не тестировался. Возможно, вам потребуется получить значения в полях и сравнить их, а не просто сравнивать поля друг с другом, но теоретически это должно работать.

Эти библиотеки должны помочь.

https://code.google.com/p/beandiff/ - библиотека сравнения компонентов на основе аннотаций. Лицензия Apache 2.0

https://github.com/SQiShER/java-object-diff/ - bean-компонент различается в зависимости от шаблона посетителя. Лицензия Apache 2.0

У нас было требование генерировать разницу между bean-компонентами в формате json для целей аудита. В итоге мы реализовали его с помощью библиотеки beandiff .

** РЕДАКТИРОВАТЬ ** Это похоже на более новый вариант. Но я не использовал его.

http://beandiff.org/

Надеюсь, это поможет.

Решение с использованием отражения и стандартных структур данных.

    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);

Хорошие ответы выше.

Если ваши данные структурно меняются, то есть целые коллекции полей могут быть релевантными или не зависеть от других, вы можете рассмотреть возможность дифференциального выполнения .

По сути, у вас есть цикл по полям, и вы сериализуете текущие значения поля одновременно с десериализацией предыдущих значений, сравнивая их по ходу.

Если есть условный тест, который делает блок полей релевантным или нет, вы сериализуете / десериализуете истинное или ложное значение условного теста и используете его, чтобы решить, следует ли сериализовать и / или десериализовать затронутые поля. И это красиво повторяется.

Просто предложение.

Отражение не поддерживает порядок полей при следующем вызове: это более безопасный порядок массивов.

/*
*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 вы можете определить логику сортировки (КАК СОРТИРОВАТЬ ЭЛЕМЕНТЫ) с помощью внутреннего класса Ordinator, расширяющего Comparator .

PS код это черновик

Мы сделали нечто подобное с утилитами bean-компонентов, и это хорошо сработало. На что следует обратить внимание: вы детализируете объекты полей - если человек содержит адрес, и адрес изменяется, вы говорите, что адрес изменился или изменился адрес address.postalCode (мы это делаем)? Вы возвращаете имя свойства списка, старое значение, новое значение из diff (мы делаем)? Как вы хотите обрабатывать даты - если все, что вас волнует, это часть даты, тогда ваше сравнение должно игнорировать время? Как сказать, какие поля игнорировать?

На самом деле это не ответ копирования и вставки, а скорее список вещей, которые не были сразу очевидны, когда мы писали свое различие.

Что касается реализации, у нас есть просто статический метод util, который принимает два bean-компонента и список свойств для сравнения, а затем возвращает карту свойств в Pair, содержащую старое значение и новое значение. Затем у каждого bean-компонента есть diff(Object o) метод, который при необходимости вызывает статический метод util.