Framework Java générique pour gérer les associations bidirectionnelles et les mises à jour inverses

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

Question

Je recherchais un moyen générique de gérer les associations bidirectionnelles et de gérer les mises à jour inverses dans le code Java écrit manuellement.

Pour ceux qui ne savent pas de quoi je parle, voici un exemple. Ci-dessous figurent les résultats actuels de solutions (insatisfaisantes).

public class A {
    public B getB();
    public void setB(B b);
}

public class B {
    public List<A> getAs();
}

Maintenant, lors de la mise à jour d'une extrémité de l'association, afin de maintenir la cohérence, l'autre extrémité doit également être mise à jour. Soit manuellement à chaque fois

a.setB(b);
b.getA().add(a);

ou en plaçant le code correspondant dans le setter / getter et en utilisant une implémentation personnalisée de la liste.

J'ai trouvé un projet obsolète et non maintenu dont les dépendances ne sont plus disponibles ( https: //e-nspire-gemini.dev.java.net/ ). Il résout le problème en utilisant des annotations utilisées pour injecter automatiquement le code nécessaire.

Quelqu'un sait-il qu'il existe un autre cadre qui traite cette question de manière générique et discrète, à la manière de gemini?

ciao, Elmar

Était-ce utile?

La solution

collections Google (à partir du code interne de Google) - http://code.google. com / p / google-collections / est compatible avec les génériques Java (non seulement compatible, mais utilise très bien les génériques)

Classe 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 permet des associations bidirectionnelles.

Certaines de ces classes devraient faire leur apparition dans JDK 7.

Autres conseils

Sauf si vous enlevez les paramètres, vous devrez fournir un mécanisme de notification d’événement. Si vos objets sont des JavaBeans, vous envisagez d’utiliser PropertyChangeSupport et de déclencher des événements de modification de propriété.

Si vous faites cela (ou avez un autre mécanisme pour détecter les changements), alors Glazed Lists fournit un ObservableElementList qui pourrait facilement être utilisé pour gérer la synchronisation des associations à partir de la fin de la liste (c.-à-d. l'ajout de A à la liste < A > automatiquement appelle a.setB (b)). L’autre direction est facilement gérée grâce à la surveillance des modifications de propriété (ou l’équivalent).

Je réalise que ce n'est pas une solution générique, mais il semble que ce serait une base facile pour une.

Notez que quelque chose comme ceci exigerait une implémentation de liste spéciale dans la classe B - il ne fait aucun doute que les solutions de type AOP peuvent être gérées dans le cas général (par exemple, en utilisant ArrayList ou quelque chose comme ça ).

Je tiens également à souligner que vous tentez de réaliser quelque chose du Saint Graal de la liaison de données. Il existe quelques implémentations décentes pour la liaison au niveau du terrain (des choses telles que les accesseurs et les setters) (voir JGoodies binding et JSR 295 pour des exemples). Il existe également une très bonne implémentation pour la liaison de type liste (Listes émaillées, mentionnée ci-dessus). Nous utilisons les deux techniques de manière concertée dans presque toutes nos applications, mais nous n’avons jamais essayé d’être aussi abstraits que ce que vous demandez.

Si je concevais ceci, je regarderais quelque chose comme ceci:

AssociationBuilder.createAssociation(A a, Connector< A> ca, B b,  Connector< B> cb, Synchronizer< A,B> sync)

Connector est une interface qui permet de créer une seule interface pour différents types de notification de changement. Synchronizer est une interface appelée pour s’assurer que les deux objets sont synchronisés chaque fois que l’un d’eux est modifié.

sync(ChangeInfo info, A a, B b) // make sure that b reflects current state of a and vice-versa.  

ChangeInfo fournit des données sur le membre modifié et sur les modifications réellement apportées. Nous sommes. Si vous essayez vraiment de garder ce générique, alors vous devez quasiment l'implémenter jusqu'à l'utilisateur du framework.

Avec ce qui précède en place, il serait possible d’avoir un certain nombre de connecteurs et de synchroniseurs prédéfinis qui répondent à différents critères de liaison.

Fait intéressant, la signature de la méthode ci-dessus est assez similaire à l'appel de la méthode JSR 295 createAutoBinding (). Les objets de propriété sont l'équivalent de Connector. JSR 295 ne dispose pas du synchroniseur (à la place, il a une stratégie de liaison spécifiée comme ENUM - plus. JSR 295 ne fonctionne qu'avec la propriété - & Gt; binding, essayant de lier la valeur de champ d'un objet à la liste de cet objet. l'appartenance à un autre objet n'est même pas sur la table pour eux).

Pour donner un sens, ces calculs seront des pairs. Je suggère un mécanisme de paquetage privé (en l’absence d’un ami) pour rester cohérent.

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

(Disclaime: non testé ni même compilé.)

Principalement sans danger pour les exceptions (peut fuir dans les cas exceptionnels). La sécurité des threads, plusieurs, les performances, la bibliothèque, etc. est laissée comme un exercice au lecteur intéressé.

Merci pour toutes vos suggestions. Mais aucun n’est proche de ce que je cherchais, j’ai probablement formulé la question de manière erronée.

Je cherchais un remplaçant pour Gemini, donc un moyen de gérer cela de manière non intrusive, sans polluer le code avec des vérifications sans fin et des implémentations spéciales de la liste. Cela nécessite bien sûr une approche basée sur les AOP, comme le suggère Kevin.

Lorsque j’ai cherché un peu plus, j’ai trouvé sur cnet un paquet de Gemini qui contient toutes les sources et dépendances avec les sources. Les sources manquantes pour les dépendances étaient la seule préoccupation qui m'empêchait de l'utiliser. Depuis que toutes les sources sont disponibles, les bogues peuvent être corrigés. Si quelqu'un cherche ceci: http://www.download.com/Gemini /3000-2413_4-10440077.html

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top