Progetta il disegno per il comportamento polimorfico e consente la separazione delle librerie

9

Diciamo che ho una gerarchia delle classi Item : Rectangle, Circle, Triangle . Voglio essere in grado di disegnarli, quindi la mia prima possibilità è aggiungere un metodo virtuale Draw() a ciascuno:

class Item {
public:
   virtual ~Item();
   virtual void Draw() =0; 
};

Tuttavia, voglio dividere la funzionalità di disegno in una libreria Draw separata, mentre la libreria Core contiene solo le rappresentazioni di base. Ci sono un paio di possibilità a cui posso pensare:

1 - Un DrawManager che prende una lista di Item s e deve usare dynamic_cast<> per capire cosa fare:

class DrawManager {
  void draw(ItemList& items) {
    FOREACH(Item* item, items) {
       if (dynamic_cast<Rectangle*>(item)) {
          drawRectangle();
       } else if (dynamic_cast<Circle*>(item)) {
          drawCircle();
       } ....
    }
  }
};

Questo non è l'ideale in quanto si basa su RTTI e impone a una classe di essere a conoscenza di tutti gli elementi nella gerarchia.

2 - L'altro approccio è il rinvio della responsabilità di disegno a una gerarchia ItemDrawer ( RectangleDrawer , ecc.):

class Item {
   virtual Drawer* GetDrawer() =0;
}

class Rectangle : public Item {
public:
   virtual Drawer* GetDrawer() {return new RectangleDrawer(this); }
}

Ciò ottiene la separazione delle preoccupazioni tra la rappresentazione di base degli Elementi e il codice per il disegno. Il problema però è che le classi Item dipendono dalle classi del disegno.

Come posso separare questo codice di disegno in una libreria separata? La soluzione per gli articoli restituisce una classe factory di qualche descrizione? Tuttavia, come può essere definito in modo tale che la libreria Core non dipenda dalla libreria Draw?

    
posta the_mandrill 01.02.2013 - 12:41
fonte

4 risposte

3

Dai un'occhiata al schema dei visitatori .

La sua intenzione è quella di separare un algoritmo (nel tuo caso disegno) da una struttura oggetto su cui opera (elementi).

Quindi un semplice esempio per il tuo problema potrebbe essere:

class Item
{
public:
    virtual void visit(Visitable v)
    {
        v.accept(*this)
    }
};

class Rectangle : public Item
{
public:
    virtual void visit(Visitable v)
    {
        v.accept(*this)
    }
};

class Circle : public Item
{
public:
    virtual void visit(Visitable v)
    {
        v.accept(*this)
    }
};


class Visitable
{
public:
    virtual void accept(Rectangle& r);
    virtual void accept(Circle& c);
};

class Drawer : public Visitable
{
public:
    void accept(Rectangle& r)
    {
        // draw rectangle
    }   
    void accept(Circle& c)
    {
        // draw circle
    }
};


int main()
{
    Item* i1 = new Circle;
    Item* i2 = new Rectangle;

    Drawer d;
    i1->visit(d); // Draw circle
    i2->visit(d); // Draw rectangle

    return 1;
}
    
risposta data 01.02.2013 - 15:14
fonte
2

La divisione che descrivi - in due gerarchie parallele - è essenzialmente il schema del ponte .

Ti preoccupi che faccia dipendere l' astrazione raffinata (Rettangolo ecc.) dall'implementazione (RectangleDrawer), ma non sono sicuro di come potresti evitarlo completamente .

Puoi certamente fornire una fabbrica astratta per rompere questa dipendenza, ma hai ancora bisogno di un modo per dire alla fabbrica quale sottoclasse creare, e che la sottoclasse dipende ancora dall'implementazione perfezionata specifica. Cioè, anche se Rectangle non dipende da RectangleDrawer, ha ancora bisogno di un modo per dire alla fabbrica di crearne uno, e RectangleDrawer dipende ancora da Rectangle.

Vale anche la pena di chiedersi se questa sia un'astrazione utile. Tracciare un rettangolo così difficile che vale la pena inserire una classe diversa, o stai davvero cercando di astrarre la libreria grafica?

    
risposta data 01.02.2013 - 13:49
fonte
1

Dai un'occhiata a Polymorphism basato su concetti e semantica basata su concetti .

Questo lavoro ha cambiato il modo in cui guardo al polimorfismo. Abbiamo usato questo sistema per ottenere grandi risultati in situazioni ripetute.

    
risposta data 02.02.2013 - 08:34
fonte
0

Il motivo per cui stai riscontrando un problema è perché gli oggetti non devono disegnare da soli. Disegnare qualcosa correttamente dipende da un miliardo di stati e un rettangolo non avrà alcuno di quei pezzi. Dovresti avere un oggetto grafico centrale che rappresenta lo stato del core come il backbuffer / depth buffer / shader / etc, e poi dargli un metodo Draw ().

    
risposta data 02.02.2013 - 11:13
fonte

Leggi altre domande sui tag