Универсальная Java Framework для управления двунаправленными ассоциациями и обратными обновлениями - PullRequest
1 голос
/ 04 октября 2008

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

или поместив соответствующий код в установщик / получатель и используйте пользовательскую реализацию List.

Я нашел устаревший, не поддерживаемый проект, зависимости которого больше не доступны (https://e -nspire-gemini.dev.java.net / ). Он решает проблему с помощью аннотаций, которые используются для автоматического ввода необходимого кода.

Кто-нибудь знает другую структуру, которая занимается этим в общем, ненавязчивом виде аля близнецы?

чао, Эльмар

Ответы [ 4 ]

5 голосов
/ 04 октября 2008

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 / общий / собирать / пакет-summary.html допускает двунаправленные ассоциации.

Ожидается, что некоторые из этих классов попадут в JDK 7.

0 голосов
/ 06 октября 2008

Спасибо за все предложения. Но никто не подошел к тому, что я искал, я, вероятно, сформулировал вопрос неправильно.

Я искал замену Близнецам, чтобы найти способ сделать это ненавязчивым способом, не загрязняя код бесконечными проверками и специальными реализациями List. Это требует, конечно, подхода, основанного на АОП, как предлагает Кевин.

Когда я немного осмотрелся, то нашел в cnet пакет gemini, который содержит все источники и зависимости от источников. Отсутствующие источники для зависимостей были единственной проблемой, которая помешала мне использовать его. Так как теперь все источники доступны, ошибки могут быть исправлены. Если кто-то ищет это: http://www.download.com/Gemini/3000-2413_4-10440077.html

0 голосов
/ 04 октября 2008

Чтобы иметь смысл, эти calsses будут пэрами. Я предлагаю частно-пакетный механизм (в отсутствие друга) для обеспечения согласованности.

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

(Отказ от ответственности: не проверено или даже не скомпилировано.)

В большинстве случаев исключительная ситуация (в исключительном случае возможна утечка). Безопасность потоков, многие-многие, производительность, библиотека и т. Д. Оставлены заинтересованным читателям в качестве упражнения.

0 голосов
/ 04 октября 2008

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

Если вы делаете это (или имеете какой-то другой механизм для обнаружения изменений), то Glazed Lists предоставляет ObservableElementList , который можно легко использовать для обработки синхронизации ассоциации с конца списка (т.е. добавление A в список автоматически вызывает a.setB (b)). Другое направление легко обрабатывается с помощью мониторинга изменения свойств (или эквивалентного ему).

Я понимаю, что это не универсальное решение, но кажется, что оно будет легким основанием для него.

Обратите внимание, что что-то вроде этого потребует специальной реализации списка в классе B - ни в коем случае не меньше решений типа AOP, которые вы могли бы обработать в общем случае (например, используя ArrayList или что-то подобное) .

Я также должен отметить, что то, что вы пытаетесь достичь, является чем-то вроде святого Грааля привязки данных. Существует несколько достойных реализаций для связывания на уровне полей (например, геттеры и сеттеры) (см. Пример привязки JGoodies и JSR 295). Существует также одна действительно хорошая реализация для связывания типов списков (Glazed Lists, упомянутые выше). Мы используем оба метода в сочетании друг с другом почти во всех наших приложениях, но никогда не пытались стать настолько абстрактными, как то, о чем вы спрашиваете.

Если бы я проектировал это, я бы посмотрел на что-то вроде этого:

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 предоставляет данные о том, какой член изменился, и каковы были изменения. Мы. Если вы пытаетесь действительно сохранить этот универсальный шаблон, то вам в значительной степени придется реализовать его до пользователя фреймворка.

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

Интересно, что приведенная выше сигнатура метода очень похожа на вызов метода JSR 295 createAutoBinding (). Объекты свойств являются эквивалентом Connector. JSR 295 не имеет синхронизатора (вместо этого у них есть стратегия привязки, заданная как ENUM - плюс JSR 295 работает только с привязкой свойства-> свойства, пытаясь привязать значение поля одного объекта к членству списка этого объекта в другом объекте даже не на столе для них).

...