2010-09-06 11 views
6

Ho un problema con 2 classi che una volta erano ben separate, ma ora vogliono accoppiarsi.Quando le classi vogliono unire

Senza entrare troppo nello specifico del problema, qui è:

Ho usato per avere un triangolo di classe che conteneva 3 vertici spazio posizioni.

C'erano molte istanze Triangolo nel programma, quindi ciascuna aveva mantenuto la propria copia dei loro vertici. Funzioni membro come getArea(), getCentroid() ecc. Sono state scritte nella classe Triangle e poiché ciascuna istanza di Triangle ha copie dei vertici a, bec, la ricerca dell'area o del centroide non ha avuto alcuna dipendenza da altre classi. Come dovrebbe essere!

Quindi volevo passare a una rappresentazione in stile vertex-array/index buffer, per altri motivi. Ciò significa che tutti i vertici sono memorizzati in un singolo array situato in un oggetto Scene e che ogni Triangle conserva solo REFERENCES sui vertici in Scene, non copie dei vertici stessi. In un primo momento, ho provato il passaggio per i puntatori:.

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

(Nel caso vi stiate chiedendo circa i benefici, l'ho fatto per motivi per lo più a che fare con i triangoli che condividono i vertici Se * poi un muove tutti i triangoli che usa quel vertice sono aggiornati automaticamente).

Questa sarebbe stata una soluzione davvero buona! Ma non ha funzionato in modo affidabile, because std::vector invalidates pointers, e stavo usando un vettore std :: per l'elenco dei vertici principali nella classe Scene.

Così ho dovuto usare interi:

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

Ma ora ho questo nuovo problema di accoppiamento: per trovare la propria area o baricentro, classe di accesso Triangle bisogno di class Scene dove prima non ha fatto. Sembra che abbia fatto qualcosa di fsck, ma non proprio.

WWYD?

+0

Come è agnostico questo linguaggio? –

+0

modificato in C++. – bobobobo

+0

Queste classi mi ricordano George Costanza: "Ho accoppiato! Ho accoppiato!" –

risposta

3

Mi sembra che il tuo Triangolo dipenda realmente dalla tua Scena (poiché i suoi vertici sono tutti membri di quella particolare scena), quindi non c'è da vergognarsi nel fare l'oggetto. In effetti, probabilmente darei al Triangolo un membro obbligatorio di Scene *.

+0

No. Il triangolo dipende solo dalla collezione di vertici nella scena, non dalla scena stessa. Modella la collezione separatamente e puoi rompere l'apparente accoppiamento tra Triange e Scene. Vedi la risposta di Martin York. – camh

0

Supponendo di disporre di un solo Scene, è possibile trasformarlo in un oggetto singleton e accedere all'elenco dei vertici tramite metodi statici.

Se si dispone di più oggetti Scene, ogni Triangle appartiene esattamente a uno Scene - e deve "sapere" a quale scena appartiene. Pertanto, è necessario inizializzare ogni triangolo con un riferimento Scene e memorizzarlo come membro della classe.

+0

-1, wtf singleton qui ?? –

+0

Singleton OVUNQUE! – bobobobo

4

Perché non solo lo vector in Scene memorizza solo i puntatori?

std::vector<Vertex *> masterVertexList; 

In questo modo, Triangle possono continuare a utilizzare Vertex * 's e tutto quello che dovete fare è assicurarsi che i puntatori sono cancellati in Scene' distruttore s.

+0

Questo è un ottimo suggerimento! Il motivo per cui li ho mantenuti come semplici 'Vertex' e non' Vertex * 'mi piacerebbe essere in grado di passare' masterVertexList' come un semplice array alla GPU al momento del disegno, (usando '& masterVertexList [0]') – bobobobo

+1

o meglio, 'vector >' – tenfour

+0

Poiché i vertici sono generalmente condivisi da triangoli (adiacenti). – Dummy00001

1

Il passaggio da non accoppiato a accoppiato è un risultato naturale della decisione di condividere i vertici laddove possibile. Precedentemente, ogni triangolo "possedeva" i suoi vertici e la scena (presumibilmente) possedeva un mazzo o triangoli.

Permettere ai triangoli di condividere i vertici cambia il modello fondamentale - quando/se un vertice può essere condiviso tra due o più triangoli, nessun triangolo può più possedere quel vertice. Anche se è possibile (ad esempio, con qualcosa di simile a shared_ptr) per avere uno schema di proprietà condivisa, quello che stai facendo in questo momento è probabilmente più semplice: ogni vertice ha ancora un singolo proprietario, ma il proprietario è ora la scena invece del triangolo individuale.

Poiché un triangolo è ora solo un modo conveniente di raggruppare alcuni vertici nella collezione "possedere" invece di possedere i vertici stessi, non è una sorpresa che ci sia un accoppiamento più stretto tra il triangolo e la collezione che possiede i suoi vertici. Se ci tieni molto a riguardo, potresti comunque nascondere la proprietà condivisa per mantenere almeno l'aspetto del tuo precedente accoppiamento perdente.

L'idea generale sarebbe abbastanza semplice: invece di ogni triangolo conoscendo la scena che contiene i vertici del triangolo, si creerebbe una classe proxy vertice che combini un ID scena e un indice vertice, in modo che il triangolo possa manipolare il vertice oggetto proxy proprio come in precedenza avrebbe l'oggetto vertice. Non si elimina completamente l'accoppiamento più stretto, ma si isola la "conoscenza" dell'accoppiamento più stretto a una singola classe, ovvero solo responsabile del mantenimento dell'aspetto dell'accoppiamento più lento.

L'ovvia lacuna di questo sarebbe che gli oggetti proxy di vertice sono suscettibili di memorizzare una discreta quantità di dati ridondanti. Ad esempio, tutti i proxy dei vertici in un particolare triangolo rappresentano chiaramente i vertici nella stessa scena. Se memorizzi l'ID scena esplicitamente per ogni proxy del vertice, stai memorizzando tre copie dell'ID scena invece di una che avresti avuto in precedenza. A volte vale la pena - altri no. Se è un problema reale, potresti provare a trovare un modo per evitare di memorizzare l'ID scena in modo esplicito, ma questo probabilmente implicherà alcuni trucchi che non sono (anche vicini) agnostici del linguaggio.

4

È possibile passare il vettore al triangolo nel suo costruttore in modo che possa mantenere un riferimento al vettore. Quindi non è necessario accedere o conoscere una scena.

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

Questo è il modo per farlo. Modella il concetto di una collezione di vettori, indipendente dalla scena. Ora i triangoli sono accoppiati solo a VertexContainer, non a Scene. VertexContainer è l'unico * aspetto * di Scene che Triangle ha bisogno di sapere, quindi separa VertexContainer e Scene e ha solo il triangolo a conoscenza di VertexContainer. – camh

+0

... e int dovrebbe essere VertexContainer :: size_type – camh

+0

Se tutti i triangoli condividono lo stesso elenco di vertici, è necessario rendere statico il riferimento. –

1

Se si sta solo aggiungendo o togliendo alle estremità della lista vertice, utilizzare un deque invece.

+0

non è garantito che tutti gli elementi siano presenti in posizioni di memoria contigue. Questo requisito contiguo sembra essere stato aggiunto nei commenti della risposta di George Edison. – camh

1

Non penso sia troppo male. Triangle ha perso un po 'di generalità ed è diventato una classe periferica di Scene, ma se non è usato come interfaccia esterna (e quel tipo di collegamento ai buffer interni non lo suggerisce), questa è solo un'evoluzione naturale.

La mia soluzione sarebbe simile alla tua sotto il cofano, ma con più zucchero.

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

In questo modo, non c'è bisogno di riorganizzare le cose in memoria, e adattare il vecchio codice richiede semplicemente l'aggiunta di ()-->a e .a, ecc, che può essere fatto da ricerca e sostituzione, e migliora OO stile comunque.

Oppure, eliminare il costruttore e private e renderlo POD.

+0

+1 per la sintassi pazza valida ... che è pazzesca. –

+0

giusto ... non voglio dare l'impressione che lo farei comunque ... mi sono solo imbattuto mentre modificavo il codice di OP e sono troppo pigro per riempire le definizioni. – Potatoswatter

Problemi correlati