Wie dynamic_cast verhindern/vermeiden?



  • Hallo!

    Folgende Klassenstruktur:

    class ObjBase
    { }; 
    
    class Line : public ObjBase {
    // spezifische Attribute
    };
    
    class Arc : public ObjBase {
    // spezifische Attribute
    }
    
    class Polygon : public ObjBase {
    // spezifische Attribute
    }
    
    std::vector<ObjBase*> objekte;
    
    void malen()
    {
      ObjBase* o = NULL;
      for(int i = 0; i < objekte.size(); i++)
      {
        o = objekte[i];
        if(Arc* a = dynamic_cast<Arc>(o))
           ; // Spezifische Funktion zum Bogen malen
        else if(Line* l = dynamic_cast<Line>(o))
          ; // Spezifische Funmktion zum Linie malen
        else if(Polygon* p = dynamic_cast<Polygon>(o))
          ; // Spezifisch zum  Polygon malen
        else
          throw std::runtime_error("Unbekanntes Objekt!");
      }
    }
    

    Wie kann ich in der Funktion malen die Fallunterscheidung vermeiden? Eine Möglichkeit ist, virtual void ObjBase::Draw() = 0 zu definieren und dann in den einzelnen Objekten zu implementieren.

    Ich möchte das aber nicht, da es sein kann, dass später verschiedene Arten existieren, ein Objekt dazustellen (bspw. überall nur gestrichelte Linien malen). Daher möchte ich die ObjektDaten unabhängig von deren Darstellung speichern.

    Welches Pattern oder welche Möglichkeit bietet sich hier an?

    Liebe Grüße, Maxi



  • Edit: zu schnell gelesen, sorry war müll.



  • Firefighter schrieb:

    Du brauchst dort nicht rumcasten.Schau mal nach virtuellen Funktionen und Polymorphismus, das erledigt das alles für dich.

    Lies bitte seinen Post durch. Dies schließt er aus.

    Aber es geht nunmal nicht anders. Was aber geht ist sich an eine Regel zu halten: Wenn sich etwas nicht direkt umsetzen lässt, füge eine weitere Ebene der Indirektion hinzu.

    Aber etwas anderes: <ironie>ObjBase ist ein wirklich sprechender Bezeichner</ironie>.
    Gewöhn dir solche Bezeichner lieber gleich ab, und wähle sprechende Bezeichner die wirklich aussagen, wozu eine Klasse dient. Ein allgemeines "ObjBase" ist ein Fehldesign.
    Zudem: Globale Variablen sind pfui.

    Wenn wir also nichts an der Polymorphie ändern können, was könnte "eine weitere Ebene der Indirektion" sein? Du könntest z.B. die Trennung zwischen Daten und Darstellung auf die Ebene unterhalb verlagern (Sei es das du den Klassen ein Member "Zeichenstil" usw. verpasst, sei es das du die Zeichenfunktion als verweis speicherst...).

    cu André



  • Sowas in der Art?

    class Line;
    class Polygon;
    
    class IRenderer
    {
    public:
    	virtual void Draw( Line& line ) = 0;
    	virtual void Draw( Polygon& poly ) = 0;
    };
    
    class Object
    {
    public:
    	virtual void Draw( IRenderer& drawing ) = 0;
    };
    
    class Line : public Object
    {
    public:
    	void Draw( IRenderer& drawing )
    	{
    		drawing.Draw( *this );
    	}
    };
    
    class Polygon : public Object
    {
    public:
    	void Draw( IRenderer& drawing )
    	{
    		drawing.Draw( *this );
    	}
    };
    
    class Renderer : public IRenderer
    {
    public:
    	void Draw( Line& line ) 
    	{
    		// Zeichencode für linien
    	}
    
    	void Draw( Polygon& poly )
    	{
    		// Zeichencode für polygone
    	}
    };
    


  • Maxi schrieb:

    [...]Möglichkeit ist, virtual void ObjBase::Draw() = 0 zu definieren und dann in den einzelnen Objekten zu implementieren.

    Ich möchte das aber nicht, da es sein kann, dass später verschiedene Arten existieren, ein Objekt dazustellen (bspw. überall nur gestrichelte Linien malen). Daher möchte ich die ObjektDaten unabhängig von deren Darstellung speichern.
    [...]

    Ich sehe da keinen Widerspruch drin. Du kannst doch in den jeweilgen Implementierungen von Draw() dafür sorgen, dass die jeweiligen Objekte gemäß dem derzeitigen Stand ihrer Attribute gezeichnet werden. Gegebenenfalls kannst Du in der jeweiligen virtuellen Funktion die zum aktuellen Status passenden nicht-virtuellen Funktionen eines Objekts aufrufen.



  • vielen Dank für die konstruktiven Vorschläge!

    @asc: die Klassen heißen in Wirklichkeit nicht so :), war hier nur ein Beispiel um das Problem darzustellen. Außerdem hab ich auch keine globale Variable mit der Objekt-Liste, hier auch nur um zu zeigen, dass "irgendwo" ein paar objekte existieren.
    Den Objekten einen Zeichenstil zuweisen, wäre das selbe Problem, wie ich im folgenden Quote anspreche:

    Tachyon schrieb:

    Du kannst doch in den jeweilgen Implementierungen von Draw() dafür sorgen, dass die jeweiligen Objekte gemäß dem derzeitigen Stand ihrer Attribute gezeichnet werden. Gegebenenfalls kannst Du in der jeweiligen virtuellen Funktion die zum aktuellen Status passenden nicht-virtuellen Funktionen eines Objekts aufrufen.

    Das hatte ich auch schon überlegt, aber stell dir folgendes vor: du hast 1000 Objekte, Linien, Bögen etc. und willst auf einmal alle nur noch mit gestrichelten Linien darstellen. Dann müsstest du jedem Objekt sagen, dass es sich auf einmal anders darzustellen hat, und danach den Zustand wieder zurücksetzen. Trennt man die Darstellung auf, muss man nur das "Darstellobjekt" ändern.

    @David_pb: Ich glaube, das sieht ziemlich cool aus. Ist ohne Fallunterscheidung und trotzdem durchsichtig. Ist das sowas wie das visitor-pattern? Es kommt mir ein bisschen so vor (auch wenn ichs nicht ganz genau kenne).

    Liebe Grüße, Maxi



  • Maxi schrieb:

    @David_pb: Ich glaube, das sieht ziemlich cool aus. Ist ohne Fallunterscheidung und trotzdem durchsichtig. Ist das sowas wie das visitor-pattern? Es kommt mir ein bisschen so vor (auch wenn ichs nicht ganz genau kenne).

    Ja, richtig erkannt! 😉



  • Maxi schrieb:

    @David_pb: Ich glaube, das sieht ziemlich cool aus. Ist ohne Fallunterscheidung und trotzdem durchsichtig. Ist das sowas wie das visitor-pattern? Es kommt mir ein bisschen so vor (auch wenn ichs nicht ganz genau kenne).

    Ja, die Struktur entspricht genau dem Visitor-Pattern. (Könnte man auch als Kombination aus Strategy und Flyweight (ok, nicht ganz: ohne Factory und Sharing) interpretieren. Hieraus ergibt sich auch die Möglichkeit, das Flyweight wegzulassen, so dass jedes Objekt seine Zeichenstrategie enthält.)


Anmelden zum Antworten