Универсальный Java-фреймворк для управления двунаправленными ассоциациями и обратными обновлениями
-
05-07-2019 - |
Вопрос
Я искал общий способ работы с двунаправленными ассоциациями и способ обработки обратных обновлений в написанном вручную Java-коде.
Для тех, кто не знает, о чем я говорю, вот пример.Ниже приведены мои текущие результаты (неудовлетворительных) решений.
public class A {
public B getB();
public void setB(B b);
}
public class B {
public List<A> getAs();
}
Теперь, при обновлении любого конца ассоциации, чтобы поддерживать согласованность, другой конец также должен быть обновлен.Либо вручную каждый раз
a.setB(b);
b.getA().add(a);
или поместив соответствующий код в установщик / получатель и используя пользовательскую реализацию списка.
Я нашел устаревший, не поддерживаемый проект, зависимости которого больше недоступны (https://e-nspire-gemini.dev.java.net/).Он решает проблему с помощью аннотаций, которые используются для автоматического ввода необходимого кода.
Кто-нибудь знает о другом фреймворке, который справляется с этим общим, ненавязчивым способом ala gemini?
ciao, Elmar
Решение
коллекции Google (из внутреннего кода Google) -- http://code.google.com/p/google-collections/ совместимы ли Java Generics (не только совместимы, но и очень хорошо используют дженерики)
Класс BiMap -- http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/package-summary.html допускает двунаправленные ассоциации.
Ожидается, что некоторые из этих классов войдут в JDK 7.
Другие советы
Если вы не абстрагируетесь от установщиков, вам придется предоставить какой-то механизм уведомления о событиях.Если ваши объекты являются JavaBeans, то вы рассматриваете возможность использования PropertyChangeSupport и запуска событий изменения свойств.
Если вы сделаете это (или у вас есть какой-либо другой механизм для обнаружения изменений), то Glazed Lists предоставит Список наблюдаемых элементов это можно было бы легко использовать для обработки синхронизации ассоциации с конца списка (т.е.добавление A в список< A> автоматически вызывает a.setB(b)).С другим направлением легко справиться с помощью мониторинга изменения свойств (или аналогичного).
Я понимаю, что это не универсальное решение, но, похоже, это было бы простой основой для одного из них.
Обратите внимание, что что-то подобное будет требовать специальная реализация списка в классе B - никаких решений типа AOP, с которыми вы могли бы справиться в общем случае (т.е.используя ArrayList или что-то в этом роде).
Я должен также отметить, что то, чего вы пытаетесь достичь, является чем-то вроде святого грааля привязки данных.Существует несколько достойных реализаций для привязки на уровне полей (такие вещи, как геттеры и сеттеры) (см. JGoodies binding и JSR 295 для примеров).Существует также одна действительно хорошая реализация для привязки типа списка (Застекленные списки, упомянутые выше).Мы используем оба метода согласованно друг с другом почти во всех наших приложениях, но никогда не пытались сделать это настолько абстрактно, как то, о чем вы спрашиваете.
Если бы я проектировал это, я бы посмотрел на что-то вроде этого:
AssociationBuilder.createAssociation(A a, Connector< A> ca, B b, Connector< B> cb, Synchronizer< A,B> sync)
Соединитель - это интерфейс, который обеспечивает единый интерфейс для различных типов уведомлений об изменениях.Synchronizer - это интерфейс, который вызывается для того, чтобы убедиться, что оба объекта синхронизированы всякий раз, когда изменяется один из них.
sync(ChangeInfo info, A a, B b) // make sure that b reflects current state of a and vice-versa.
ChangeInfo предоставляет данные о том, какой участник изменился, и каковы были изменения на самом деле.Мы такие.Если вы действительно пытаетесь сохранить это общее, то вам в значительной степени придется доверить реализацию этого пользователю фреймворка.
С учетом вышесказанного было бы возможно иметь ряд предопределенных соединителей и синхронизаторов, которые отвечают различным критериям привязки.
Интересно, что вышеупомянутая сигнатура метода очень похожа на вызов метода createAutoBinding() в JSR 295.Объекты свойств являются эквивалентом соединителя.В JSR 295 нет синхронизатора (вместо этого у них есть стратегия привязки, указанная в виде ПЕРЕЧИСЛЕНИЯ - плюс JSR 295 работает только с property-> привязка свойств, пытаясь привязать значение поля одного объекта к членству этого объекта в списке в другом объекте, которого у них даже нет в таблице).
Чтобы было логично, эти calsses будут равными.Я предлагаю пакетно-частный механизм (в отсутствие friend) для сохранения согласованности.
public final class A {
private B b;
public B getB() {
return b;
}
public void setB(final B b) {
if (b == this.b) {
// Important!!
return;
}
// Be a member of both Bs (hence check in getAs).
if (b != null) {
b.addA(this);
}
// Atomic commit to change.
this.b = b;
// Remove from old B.
if (this.b != null) {
this.b.removeA(this);
}
}
}
public final class B {
private final List<A> as;
/* pp */ void addA(A a) {
if (a == null) {
throw new NullPointerException();
}
// LinkedHashSet may be better under more demanding usage patterns.
if (!as.contains(a)) {
as.add(a);
}
}
/* pp */ void removeA(A a) {
if (a == null) {
throw new NullPointerException();
}
as.removeA(a);
}
public List<A> getAs() {
// Copy only those that really are associated with us.
List<A> copy = new ArrayList<A>(as.size());
for (A a : as) {
if (a.getB() == this) {
copy.add(a);
}
}
return Collection.unmodifiableList(copy);
}
}
(Отказываюсь:Не тестировался и даже не компилировался.)
В основном безопасен для исключений (в исключительном случае может произойти утечка).Потокобезопасность, многое-многое другое, производительность, библиотечность и т.д. оставлены в качестве упражнения заинтересованному читателю.
Спасибо за все предложения.Но ни один из них и близко не подошел к тому, что я искал, вероятно, я неправильно сформулировал вопрос.
Я искал замену gemini, то есть способ справиться с этим ненавязчивым образом, не загрязняя код бесконечными проверками и специальными реализациями списка.Это, конечно, требует подхода, основанного на AOP, как предложил Кевин.
Когда я еще немного осмотрелся, я нашел пакет gemini в cnet, который содержит все источники и зависимости с источниками.Отсутствующие источники для зависимостей были единственной проблемой, которая помешала мне его использовать.Поскольку теперь доступны все исходные тексты, ошибки могут быть исправлены.На случай, если кто-нибудь поищет это: http://www.download.com/Gemini/3000-2413_4-10440077.html