Pregunta

He estado buscando una forma genérica de lidiar con asociaciones bidireccionales y una forma de manejar las actualizaciones inversas en código Java escrito manualmente.

Para aquellos que no saben de lo que estoy hablando, aquí hay un ejemplo. Debajo están mis resultados actuales de soluciones (insatisfactorias).

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

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

Ahora, al actualizar cualquier extremo de la asociación, para mantener la coherencia, el otro extremo también debe actualizarse. Ya sea manualmente cada vez

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

o colocando código coincidente en el setter / getter y usa una implementación de Lista personalizada.

He encontrado un proyecto desactualizado y no mantenido cuyas dependencias ya no están disponibles ( https: //e-nspire-gemini.dev.java.net/ ). Se ocupa del problema mediante el uso de anotaciones que se utilizan para inyectar el código necesario automáticamente.

¿Alguien sabe de otro marco que se ocupe de esto de una manera genérica y discreta, ala gemini?

chao, Elmar

¿Fue útil?

Solución

colecciones de google (del código interno de google) - http://code.google. com / p / google-collections / es compatible con Java Generics (no solo es compatible, usa genéricos muy bien)

Class 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 permite asociaciones bidireccionales.

Se espera que algunas de estas clases lleguen a JDK 7.

Otros consejos

A menos que resuma los establecedores, tendrá que proporcionar algún tipo de mecanismo de notificación de eventos. Si sus objetos son JavaBeans, entonces está buscando usar PropertyChangeSupport y disparar eventos de cambio de propiedad.

Si hace eso (o tiene algún otro mecanismo para detectar cambios), Glazed Lists proporciona un ObservableElementList que podría usarse fácilmente para manejar la sincronización de asociación desde el final de la lista (es decir, agregar A a List < A > automáticamente llama a.setB (b)). La otra dirección se maneja fácilmente mediante el monitoreo de cambio de propiedad (o equivalente).

Me doy cuenta de que esta no es una solución genérica, pero parece que sería una base fácil para una.

Tenga en cuenta que algo como esto requeriría una implementación de lista especial en la clase B, de ninguna manera menos de las soluciones de tipo AOP que podría manejar en el caso general (es decir, usando ArrayList o algo así ).

También debo señalar que lo que está tratando de lograr es algo del santo grial del enlace de datos. Hay algunas implementaciones decentes para la vinculación a nivel de campo (cosas como getters y setters) (consulte JGoodies vinculante y JSR 295 para ver ejemplos). También hay una implementación realmente buena para el enlace de tipo de lista (Listas acristaladas, mencionado anteriormente). Utilizamos ambas técnicas en concierto entre sí en casi todas nuestras aplicaciones, pero nunca hemos tratado de ser tan abstractos como lo que estás preguntando.

Si estuviera diseñando esto, vería algo como esto:

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

Connector es una interfaz que permite una única interfaz para varios tipos de notificación de cambios. Synchronizer es una interfaz que se llama para asegurarse de que ambos objetos estén sincronizados cada vez que se cambie uno de ellos.

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

ChangeInfo proporciona datos sobre qué miembro cambió y cuáles fueron realmente los cambios. Estamos. Si está tratando de mantener este genérico, entonces tiene que corregir la implementación de esto hasta el usuario del marco.

Con lo anterior en su lugar, sería posible tener una cantidad de Conectores y Sincronizadores predefinidos que cumplan con diferentes criterios de enlace.

Curiosamente, la firma del método anterior es bastante similar a la llamada al método createAutoBinding () de JSR 295. Los objetos de propiedad son el equivalente de Connector. JSR 295 no tiene el Sincronizador (en cambio, tienen una estrategia de enlace especificada como ENUM, además JSR 295 funciona solo con la propiedad - & Gt; enlace de propiedad, tratando de vincular un valor de campo de un objeto a la lista de ese objeto la pertenencia a otro objeto ni siquiera está sobre la mesa para ellos).

Para tener sentido, estas calsses serán pares. Sugiero un mecanismo de paquete privado (en ausencia de un amigo) para mantener la coherencia.

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

(Descargo de responsabilidad: no probado o incluso compilado)

Mayormente salvo de excepción (puede tener fugas en caso de excepción). La seguridad de subprocesos, muchos, rendimiento, biblioteca, etc., se deja como un ejercicio para el lector interesado.

Gracias por todas las sugerencias. Pero ninguno se acercó a lo que estaba buscando, probablemente formulé la pregunta de manera incorrecta.

Estaba buscando un reemplazo para gemini, por lo que era una forma de manejar esto de una manera discreta, sin contaminar el código con comprobaciones interminables e implementaciones especiales de listas. Esto requiere, por supuesto, un enfoque basado en AOP, como lo sugiere Kevin.

Cuando miré un poco más, encontré un paquete de gemini en cnet que contenía todas las fuentes y dependencias con las fuentes. Las fuentes faltantes para las dependencias fueron la única preocupación que me impidió usarlo. Como ahora todas las fuentes están disponibles, los errores se pueden corregir. En caso de que alguien busque esto: http://www.download.com/Gemini /3000-2413_4-10440077.html

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top