2015-10-04 10 views
5

Ich bin neu in diesem Muster, könnte bitte jemand mir dabei helfen?Implementieren Besucher Muster in C#

Ich habe ein Objekt wie folgt aus:

public class Object 
    { 
     public string Name { get; set; } 
     public object Value { get; set; } 
     public List<Object> Childs { get; set; } 
    } 

Hier ist ein JSON Beispiel:

{ 
    "Name": "Method", 
    "Value": "And", 
    "Childs": [{ 
     "Name": "Method", 
     "Value": "And", 
     "Childs": [{ 
      "Name": "Operator", 
      "Value": "IsEqual", 
      "Childs": [{ 
       "Name": "Name", 
       "Value": "5", 
       "Childs": [] 
      }] 
     }, 
     { 
      "Name": "Operator", 
      "Value": "IsEqual", 
      "Childs": [{ 
       "Name": "Name", 
       "Value": "6", 
       "Childs": [] 
      }] 
     }] 
    }, 
    { 
     "Name": "Operator", 
     "Value": "IsEqual", 
     "Childs": [{ 
      "Name": "Name", 
      "Value": "3", 
      "Childs": [] 
     }] 
    }] 
} 

Meine Frage, wie Besucher-Muster zu machen, um diese letzte Zeichenfolge zu erhalten:

(Name IsEqual 3)And((Name IsEqul 5)And(Name IsEqual 6)) 
+4

Ihre Frage ist nicht klar, bitte arbeiten Sie daran. Was meinen Sie, dass Name und Wert eine Methode oder ein Operator sein können. Und was meinst du mit "Ex"? Wie verhält sich "And" und "IsEqualTo" zu Ihrer Frage? –

+0

Ok, es gibt ein Schema, aber was ist die Frage? – Matyas

+0

* Vielleicht * Sie suchen nach etwas wie 'Expression Trees' https://msdn.microsoft.com/en-us/library/bb397951.aspx? "Objekt" ist übrigens kein guter Klassenname. –

Antwort

11

Besuchermuster zu implementieren müssen Sie zwei einfache Schnittstellen

  1. IVisitable mit einer Accept Methode, um die IVisitor als Parameter aufweist.
  2. IVisitor mit vielen Visit Methoden für jede Implementierung von IVisitable

So Grundidee des Besuchermusters ist das Verhalten dynamisch auf die Art der Implementierung entsprechend zu ändern.

Für Ihren Fall die Sache, die Sie besuchen möchten (die besuchbare) ist die Object Klasse, die offenbar nicht verschiedene Derivate hat und Sie möchten das Verhalten nach einem Eigenschaftswert nicht den Typ ändern. Das Besuchermuster ist also nicht das, was Sie wirklich brauchen, und ich empfehle Ihnen, die Antworten mit der rekursiven Methode zu betrachten.

Aber wenn Sie wirklich Besuchermuster hier verwenden möchten, kann es in etwa so aussehen.

interface IVisitable { void Accept(IVisitor visitor); } 

interface IVisitor { 
    void VisitAnd(Object obj); 
    void VisitEquals(Object obj); 
} 

Da die Object Klasse ist eine einfache POCO Ich nehme an, Sie werden nicht über eine Schnittstelle implementieren möchten und ein Verfahren in dieser Klasse hinzufügen. So werden Sie ein adapter Objekt benötigen, die Object zu IVisitable

class VisitableObject : IVisitable { 
    private Object _obj; 

    public VisitableObject(Object obj) { _obj = obj; } 

    public void Accept(IVisitor visitor) { 
     // These ugly if-else are sign that visitor pattern is not right for your model or you need to revise your model. 
     if (_obj.Name == "Method" && _obj.Value == "And") { 
      visitor.VisitAnd(obj); 
     } 
     else if (_obj.Name == "Method" && _obj.Value == "IsEqual") { 
      visitor.VisitEquals(obj); 
     } 
     else 
      throw new NotSupportedException(); 
     } 
    } 
} 

public static ObjectExt { 
    public static IVisitable AsVisitable(this Object obj) { 
     return new VisitableObject(obj); 
    } 
} 

Und schließlich die Besucher Implementierung wie diese

class ObjectVisitor : IVisitor { 
    private StringBuilder sb = new StringBuilder(); 

    public void VisitAnd(Object obj) { 
     sb.Append("("); 
     var and = ""; 
     foreach (var child in obj.Children) { 
      sb.Append(and); 
      child.AsVisitable().Accept(this); 
      and = "and"; 
     } 
     sb.Append(")"); 
    } 

    public void VisitEquals(Object obj) { 
     // Assuming equal object must have exactly one child 
     // Which again is a sign that visitor pattern is not bla bla... 
     sb.Append("(") 
      .Append(obj.Children[0].Name); 
      .Append(" Equals "); 
      .Append(obj.Children[0].Value); 
      .Append(")"); 
    } 
} 
0

Dies ist möglicherweise nicht das, was Sie wollen. Aber eine Möglichkeit, die Ausgabe zu erstellen, die Sie wollen, ohne das Besucher-Muster verwendet, ist die folgende Methode der Object Klasse hinzufügen, wie folgt aus:

public string Format() 
{ 
    if (Name == "Operator") 
    { 
     if(Childs == null || Childs.Count != 1) 
      throw new Exception("Invalid Childs"); 

     Object chlid = Childs[0]; 

     return chlid.Name + " IsEqual " + chlid.Value; 

    } 

    if (Name == "Method") 
    { 
     if(Childs == null || Childs.Count == 0) 
      throw new Exception("Invalid Childs"); 

     var str = " " + Value + " "; 

     return string.Join(str, Childs.Select(x => "(" + x.Format() + ")")); 
    } 

    throw new Exception("Format should only be invoked on Operator/Method"); 
} 
0

allererst Sie falschen Reihenfolge in der result.Second haben, somethimes Sie Miss Klammern im Ergebnis.Final sollte es sein:

(((Name IsEqual 5) And (Name IsEqual 6)) And (Name IsEqual 3)) 

Um diese Aufgabe abzuschließen, sollten Sie rekursive Funktion verwenden.

static IEnumerable<string> ReturnString(Obj val) 
     { 
      foreach (Obj node in val.Childs) 
       yield return ConvertToString(node); 
     } 

     static string ConvertToString(Obj val) 
     { 
      switch(val.Name) 
      { 
       case "Operator": 
        { 
         return string.Format("({0} {1} {2})", val.Childs[0].Name, val.Value, val.Childs[0].Value); 
        } 
       case "Method": 
        { 
         IEnumerable<string> coll = ReturnString(val); 
         StringBuilder final = new StringBuilder(); 
         final.Append("("); 

         IEnumerator<string> e = coll.GetEnumerator(); 
         e.MoveNext(); 
         final.Append(string.Format("{0}", e.Current, val.Value)); 

         while (e.MoveNext()) 
         { 
          final.Append(string.Format(" {0} {1}", val.Value, e.Current)); 
         } 

         final.Append(")"); 


         return final.ToString(); 
        } 
       case "Name": 
        return Convert.ToString(val.Value); 
      } 
      return "-"; 
     } 

Im Folgenden finden Sie Ihr Beispiel im Code:

string s = ConvertToString(new Obj 
      { 
       Name = "Method", 
       Value = "And", 
       Childs = new List<Obj> 
         { 
          new Obj() 
          { 
           Name = "Method", 
           Value = "And", 
           Childs = new List<Obj> 
           { 
            new Obj() 
            { 
             Name = "Operator", 
             Value = "IsEqual", 
             Childs = new List<Obj> 
             { 
              new Obj() 
              { 
               Name="Name", 
               Value="5", 
               Childs=null 
              } 
             } 
            }, 
            new Obj() 
            { 
            Name = "Operator", 
             Value = "IsEqual", 
             Childs = new List<Obj> 
             { 
              new Obj() 
              { 
               Name="Name", 
               Value="6", 
               Childs=null 
              } 
             } 
            } 
           } 
          }, 
          new Obj() 
          { 
           Name = "Operator", 
           Value = "IsEqual", 
           Childs = new List<Obj> 
           { 
            new Obj() 
            { 
             Name="Name", 
             Value="3", 
             Childs=null 
            } 
           } 
          } 
         } 
      }); 
0

Die JSON eindeutig repräsentiert einen Token Baum (von einem Parser erzeugt möglicherweise) passt aussehen .

Besucher Muster Verwendung Polymorphismus.

Um von einem Besucher-Muster verwendet werden soll, müssen Sie es deserialisieren Objekte mit unterschiedlichen Besuchsverhalten zu erhalten:

  • MethodToken
  • OperatorToken
  • NameToken

Dann sollte IVisitor die Visit-Methode für jede Methode implementieren: