Digamos que tengo dos tipos de objetos, A y B. La relación entre ellos es de muchos a muchos, pero ninguno de ellos es el dueño del otro.
Ambas instancias A y B deben ser conscientes de la conexión; No es solo una forma.
Entonces, podemos hacer esto:
class A
{
...
private: std::vector<B *> Bs;
}
class B
{
private: std::vector<A *> As;
}
Mi pregunta es: ¿ dónde pongo las funciones para crear y destruir las conexiones?
¿Debería ser A :: Attach (B), que luego actualiza A :: Bs y B :: Como vectores?
O debería ser B :: Adjuntar (A), que parece igualmente razonable.
Ninguno de los dos se siente bien. Si dejo de trabajar con el código y vuelvo después de una semana, estoy seguro de que no podré recordar si debería estar haciendo A.Attach (B) o B.Attach (A).
Quizás debería ser una función como esta:
CreateConnection(A, B);
Pero hacer una función global también parece indeseable, dado que es una función específica para trabajar solo con las clases A y B.
Otra pregunta: si me encuentro con este problema / requisito con frecuencia, ¿puedo de alguna manera hacer una solución general para ello? ¿Quizás una clase TwoWayConnection de la que puedo derivar o usar dentro de clases que comparten este tipo de relación?
¿Cuáles son algunas buenas maneras de manejar esta situación? Sé cómo manejar la situación de uno a muchos "C posee D" bastante bien, pero esta es más complicada.
Editar: solo para hacerlo más explícito, esta pregunta no involucra problemas de propiedad. Tanto A como B son propiedad de algún otro objeto Z, y Z se ocupa de todos los problemas de propiedad. Solo me interesa cómo crear / eliminar los enlaces de muchos a muchos entre A y B.
Pointer
yGestureRecognizer
. Los punteros son propiedad y están administrados por la clase InputManager. GestureRecognizers son propiedad de instancias de Widget, que a su vez son propiedad de una instancia de Screen que es propiedad de una instancia de aplicación. Los punteros se asignan a GestureRecognizers para que puedan enviarles datos de entrada sin procesar, pero GestureRecognizers deben saber cuántos punteros hay asociados actualmente con ellos (para distinguir los gestos de 1 dedo frente a 2 dedos, etc.).Respuestas:
Una forma es agregar un
Attach()
método público y también unAttachWithoutReciprocating()
método protegido a cada clase. Hacer amigosA
yB
amigos mutuos para que susAttach()
métodos puedan llamar a los demásAttachWithoutReciprocating()
:Si implementa métodos similares para
B
, no tendrá que recordar a qué clase recurrirAttach()
.Estoy seguro de que podría concluir ese comportamiento en una
MutuallyAttachable
clase que heredeA
yB
herede, evitando así repetirse y obtener puntos de bonificación en el Día del Juicio. Pero incluso el enfoque poco sofisticado de implementarlo en ambos lugares hará el trabajo.fuente
MutuallyAttachable
clase que escribí para esta tarea, si alguien quiere reutilizarla: goo.gl/VY9RB (Pastebin) Editar: Este código podría mejorarse convirtiéndolo en una clase de plantilla.MutuallyAttachable<T, U>
plantilla de clase en Gist. gist.github.com/3308058¿Es posible que la relación misma tenga propiedades adicionales?
Si es así, entonces debería ser una clase separada.
De lo contrario, cualquier mecanismo de gestión de listas recíprocas será suficiente.
fuente
Por lo general, cuando me meto en situaciones como esta que me parecen incómodas, pero no puedo decir por qué, es porque estoy tratando de poner algo en la clase incorrecta. Casi siempre hay una manera de mover algunas funciones fuera de clase
A
yB
hacer las cosas más claras.Un candidato para mover la asociación a es el código que crea la asociación en primer lugar. Quizás en lugar de llamarlo
attach()
simplemente almacene una lista destd::pair<A, B>
. Si hay múltiples lugares creando asociaciones, se puede resumir en una clase.Otro candidato para un movimiento es el objeto que posee los objetos asociados,
Z
en su caso.Otro candidato común para mover la asociación es el código que con frecuencia invoca métodos
A
uB
objetos. Por ejemplo, en lugar de llamara->foo()
, que internamente hace algo con todos losB
objetos asociados , ya conoce las asociaciones y las llamadasa->foo(b)
para cada uno. Nuevamente, si varios lugares lo llaman, se puede resumir en una sola clase.Muchas veces los objetos que crean, poseen y usan la asociación resultan ser el mismo objeto. Eso puede hacer que la refactorización sea muy fácil.
El último método es derivar la relación de otras relaciones. Por ejemplo, los hermanos y hermanas son una relación de muchos a muchos, pero en lugar de mantener una lista de hermanas en la clase de hermanos y viceversa, deriva esa relación de la relación de los padres. La otra ventaja de este método es que crea una única fuente de verdad. No hay forma posible de que un error o error de tiempo de ejecución cree una discrepancia entre las listas de hermanos, hermanas y padres, porque solo tiene una lista.
Este tipo de refactorización no es una fórmula mágica que funcione siempre. Todavía tiene que experimentar hasta que encuentre una buena opción para cada circunstancia individual, pero he descubierto que funciona aproximadamente el 95% del tiempo. El resto del tiempo solo tienes que vivir con la incomodidad.
fuente
Si implementara esto, pondría Attach en A y B. Dentro del método Attach, llamaría Attach en el objeto pasado. De esa manera puede llamar a A.Attach (B) o B.Attach ( UNA). Mi sintaxis puede no ser correcta, no he usado c ++ en años:
Un método alternativo sería crear una tercera clase, C, que administra una lista estática de emparejamientos AB.
fuente