Универсальный Java-фреймворк для управления двунаправленными ассоциациями и обратными обновлениями

StackOverflow https://stackoverflow.com/questions/169420

Вопрос

Я искал общий способ работы с двунаправленными ассоциациями и способ обработки обратных обновлений в написанном вручную 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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top