2010-09-06 10 views
6

Tengo un problema con 2 clases que alguna vez estuvieron muy bien separadas, pero ahora quieren aparearse.Cuando las clases quieren acoplar

Sin entrar demasiado en los aspectos específicos del problema, aquí está:

que solía tener un triángulo de clase que contenía 3 vértices espacio-posición.

class Triangle 
{ 
    Vertex a,b,c ; // vertices a, b and c 
} ; 

Hubo muchas instancias de Triangle en el programa, por lo que cada una había conservado su propia copia de sus vértices. Las funciones de miembro como getArea(), getCentroid() etc. se escribieron en la clase Triangle, y dado que cada instancia Triangle tenía copias de los vértices a, byc, al encontrar el área o centroide no dependía de otras clases. ¡Como debería ser!

Luego quise pasar a una representación de estilo de matriz de vértices/matriz de índices, por otros motivos. Esto significa que todos los vértices se almacenan en una única matriz ubicada en un objeto Scene, y cada Triangle solo conserva REFERENCIAS a los vértices en Scene, no copias de los propios vértices. Al principio, traté de cambiar a cabo para los punteros:.

class Scene 
{ 
    std::vector<Vertex> masterVertexList ; 
} ; 

class Triangle 
{ 
    Vertex *a,*b,*c ; // vertices a, b and c are pointers 
    // into the Scene object's master vertex list 
} ; 

(En caso de que usted se está preguntando acerca de los beneficios, lo hice por razones que ver principalmente con los triángulos que comparten vértices Si * entonces a se mueve todos los triángulos que utilizar ese vértice se actualizan automáticamente).

¡Esta habría sido una muy buena solución! Pero no funcionó de manera confiable, because std::vector invalidates pointers, y estaba usando un std :: vector para la lista de vértices maestros en la clase Scene.

Así que tuve que usar números enteros:

class Triangle 
{ 
    int a,b,c ; // integer index values 
    // into the Scene object's master vertex list 
} ; 

Pero ahora tengo este nuevo problema de acoplamiento: para encontrar su propia área o centro de gravedad, la clase de acceso a Triangle necesidades class Scene donde antes no lo hizo. Parece que he avergonzado algo, pero no realmente.

WWYD?

+0

¿En qué idioma es este un lenguaje agnóstico? –

+0

Cambiado a C++. – bobobobo

+0

Estas clases me recuerdan a George Costanza: "¡He acoplado! ¡He acoplado!" –

Respuesta

3

Me parece que su Triángulo realmente depende de su Escena (ya que sus vértices son todos miembros de esa escena en particular), así que no hay vergüenza en hacer que el objeto lo haga. De hecho, probablemente le daría al Triangle un miembro obligatorio de Scene *.

+0

No. El triángulo solo depende de la colección de vértices en la escena, no de la escena misma. Modele la colección por separado y puede romper el aparente acoplamiento entre Triange y Scene. Ver la respuesta de Martin York. – camh

0

Suponiendo que tiene solo un Scene, puede convertirlo en un objeto singleton y acceder a la lista de vértices a través de métodos estáticos.

Si tiene múltiples objetos Scene, entonces cada Triangle pertenece exactamente a uno Scene - y debería "saber" a qué escena pertenece. Por lo tanto, debe inicializar cada triángulo con una referencia Scene y almacenarlo como un miembro de la clase.

+0

-1, wtf singleton aquí ?? –

+0

Singleton EN TODAS PARTES! – bobobobo

4

¿Por qué no solo tiene vector en Scene solo para almacenar punteros?

std::vector<Vertex *> masterVertexList; 

De esa manera, Triangle puede seguir utilizando Vertex * 's y todo lo que tiene que hacer es asegurarse de que los punteros se eliminan en Scene' destructor s.

+0

¡Esta es una muy buena sugerencia! La razón por la que los mantuve como 'Vertex' simple y no' Vertex * 'era que me gustaría poder pasar' masterVertexList' como una matriz simple a la GPU en el momento del dibujo, (usando '& masterVertexList [0]') – bobobobo

+1

o más bien, 'vector >' – tenfour

+0

Porque los vértices generalmente se comparten con triángulos (adyacentes). – Dummy00001

1

El cambio de desacoplado a acoplado es un resultado natural de su decisión de compartir vértices siempre que sea posible. Anteriormente, cada triángulo "poseía" sus vértices, y la escena (presumiblemente) poseía un grupo o triángulos.

Permitir que los triángulos compartan vértices cambia ese modelo fundamental - cuando/si un vértice puede ser compartido entre dos o más triángulos, ningún triángulo puede poseer ese vértice. Aunque es posible (por ejemplo, con algo así como shared_ptr) para tener un esquema de propiedad compartida y distribuida, lo que está haciendo en este momento es probablemente más sencillo: cada vértice todavía tiene un único propietario, pero el propietario ahora es la escena en lugar del triángulo individual.

Dado que un triángulo ahora es solo una manera conveniente de agrupar algunos vértices en la colección "propietaria" en lugar de poseer los vértices, no es de sorprender que haya un acoplamiento más apretado entre el triángulo y la colección que posee sus vértices. Si le importa mucho, puede ocultar la propiedad compartida para conservar al menos la apariencia de su acoplamiento más flexible anterior.

La idea general sería bastante simple: en lugar de que cada triángulo conozca la escena que contiene los vértices del triángulo, crearía una clase proxy de vértices que combina una ID de escena y un índice de vértice, para que el triángulo pueda manipular el vértice el objeto proxy tal como lo haría anteriormente tendría el objeto vértice. No elimina por completo el acoplamiento más apretado, pero aísla el "conocimiento" del acoplamiento más apretado en una sola clase, eso es solo responsable de mantener la apariencia de un acoplamiento más flojo.

La desventaja obvia de esto sería que los objetos proxy de vértice probablemente almacenarán una buena cantidad de datos redundantes. Por ejemplo, todos los proxies de vértices en cualquier triángulo particular están representando claramente vértices en la misma escena. Si almacena la ID de escena explícitamente para cada proxy de vértice, está almacenando tres copias de la ID de escena en lugar de una que hubiera tenido previamente. A veces esto vale la pena; otros no. Si se trata de un problema real, podrías tratar de encontrar la forma de evitar el almacenamiento de la identificación de la escena de forma explícita, pero eso probablemente implique algunos trucos que no son (ni siquiera están cerca) del lenguaje agnóstico.

4

Puede pasar el vector al triángulo en su constructor para que pueda mantener una referencia al vector. Entonces no necesita acceder o conocer una Escena.

typedef std::vector<Vertex> VertexContainer; 

class Scene 
{ 
    VertexContainer masterVertexList ; 
} ; 

class Triangle 
{ 
    // A references to the vertices contained in Scene. 
    // A triangle no longer needs to know anything about a scene 
    VertexContainer& vertexListRef; 

    // index into vertexListRef of the triangles points. 
    VertexContainer::size_type a; 
    VertexContainer::size_type b; 
    VertexContainer::size_type c; 

    public: 
     Triangle(VertexContainer&   masterVertexList, 
       VertexContainer::size_type x, 
       VertexContainer::size_type y, 
       VertexContainer::size_type z) 
      :vertexListRef(masterVertexList) 
      ,a(x),b(y),c(z) 
     {} 
}; 
+0

Esta es la manera de hacerlo. Modele el concepto de una colección de vectores, independiente de la escena. Ahora los triángulos solo están acoplados a VertexContainer, no a Scene. El VertexContainer es el * único * aspecto de Scene que Triangle necesita saber, así que separe VertexContainer y Scene y haga que Triangle solo conozca VertexContainer. – camh

+0

... e int deberían ser VertexContainer :: size_type – camh

+0

Si todos los triángulos comparten la misma lista de vértices, debe hacer que la referencia sea estática. –

1

Si sólo va a añadir o quitar a los extremos de la lista de vértice, utilice un deque lugar.

+0

Deques no garantiza tener todos sus elementos en ubicaciones de almacenamiento contiguas. Ese requisito contiguo parece haber sido agregado en los comentarios de la respuesta de George Edison. – camh

1

No creo que sea tan malo. Triangle perdió algo de generalidad y se convirtió en una clase periférica de Scene, pero si no se utiliza como una interfaz externa (y ese tipo de vinculación a los búferes internos sugiere que no), eso es solo una evolución natural.

Mi solución sería similar a la suya bajo el capó, pero con más azúcar.

struct Triangle 
{ 
    Triangle(...) { ... } 
    Vertex *a(),*b(),*c() ; // trivia: this is valid syntax! Getters adjust… 
private: 
    size_t ax, bx, cx; // … offsets… 
    Scene *client; // … into the Scene object's master vertex list. 
} ; 

De esta manera, usted no tiene que reorganizar las cosas en la memoria, y la adaptación de código antiguo requiere simplemente añadiendo () a ->a y .a, etc, lo cual puede hacerse mediante la búsqueda y reemplazo, y mejora OO estilo de todos modos.

O bien, elimine el constructor y private y hágalo POD.

+0

+1 para la sintaxis válida loca ... eso es una locura. –

+0

correcto ... no quiero dar la impresión de que realmente lo haría ... tropecé con eso mientras editaba el código de OP y era demasiado flojo para completar las definiciones. – Potatoswatter

Cuestiones relacionadas