Visitor vs dynamic_cast



  • Es geht darum mathe Ausdrücke zu parsen und dann umzuformen. Welche Umformungen benötig werden steht nicht fest, allerdings ist bekannt welche Operationen in einem Ausdruck vorkommen dürfen.

    Meine Ansatz war das ganze in einen Parsebaum zu laden und diesen zu bearbeiten. In etwa wie folgendermaßen:

    class ExprBase{};
    class Sum:public ExprBase{ExprBase*a,b};
    class Product:public ExprBase{ExprBase*a,b};
    class XEvoc:public ExprBase{};
    class Num:public ExprBase{int num};
    

    So nun stellt sich die Frage wie ich den Parsebaum anfassen soll. Nach einiger Suche im Internet kam ich auf folgenden Link. Ich hab dies auf diese Weise implementiert allerdigns war alles anderes als zufrieden mit der Lösung. Der Code der zum Beispiel nötig war um einfach heraus zu finden ob ein Ausdruck von x abhängt oder nicht war einfach erschlagend. Das ständige herumgeschlage mit Visitorobjekten war auch lässtig und erschien mir alles anders als intutive.

    struct DependsOnX:public Visitor{
      DependsOnX(){result = false;}
      void visit(Product*){a->visit_by(this); b->visit_by(this);}
      void visit(Sum*){a->visit_by(this); b->visit_by(this);}
      void visit(Number*){}
      void visit(XEvoc*){result = true;}
      bool result;
    };
    //...
    DependsOnX vistor;
    expr->visit_by(visitor);
    if(visitor.result){...}
    

    Ich hab mich danach entschieden ganz einfach dynamic_cast und eine if else Kaskade zu nehmen. Erstaunlicherweise ging dies recht gut und bis auf das lässtige Gecasts auch ansonsten recht intuive. Desweiteren entfällt die ganze Visitor Hierarchie was das ganz auch einfacher macht. Es wurde sogar möglich eine Hierarchie zwischen den Operationen auszunutzen.

    class ExprBase{};
    class BinOp:ExprBase{ExprBase*a,*b};
    class Sum:public BinOp{};
    class Product:public BinOp{};
    class XEvoc:public ExprBase{};
    class Num:public ExprBase{int num};
    
    bool depends_on_x(ExprBase*expr){
      if(BinOp*op = dynamic_cast<BinOp*>(expr))
        // Folgende Zeile wäre beim Visitorpattern doppelt vorhanden.
        return depends_on_x(op->a) || depends_on_x(op->b); 
      else if(dynamic_cast<XEvoc*>(expr))
        return true;
      return false;
    }
    //...
    if(depends_on_x(expr)){...}
    

    Nachdem ich mir ein wenig des Kopf über das Visitorpattern gemacht habe bin ich zum Schluss gekommen, dass es keine Vorteile gegenüber dynamic_cast hat sogar ein paar kleine Nachteile.

    Hab ich den Zweck dieses Pattern nicht verstanden?



  • Hallo,
    Visitor lohnt sich hauptsächlich, wenn die Hierarchie (die besucht wird) stabil bleibt (also keine weiteren Klassen), aber ständig neue Funktionen hinzukommen. In diesem Fall wird die neue Funktionalität in die Besucher-Hierarchie ausgelagert (jeweils repräsentiert durch eine neue Klasse). Du schreibst also neuen Code, statt alten zu duplizieren. Ohne Visitor müsstest du deine if-else-Kaskaden ständig wiederholen (für jede neue Funktion), was die Wartung erschwert, da du nun x-Mal mehr oder weniger den selben Code akutell halten musst.

    Btw: Hast du dir mal das Interpreter-Pattern angeschaut?


Anmelden zum Antworten