2015-10-04 15 views
5

Sono nuovo in questo schema, potrebbe piacere che qualcuno mi aiuti in esso?Implementazione del modello di visitatore in C#

ho ottenuto un oggetto come questo:

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

Ecco un esempio JSON:

{ 
    "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": [] 
     }] 
    }] 
} 

La mia domanda come fare pattern Visitor al fine di ottenere questa stringa finale:

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

La domanda non è chiara, si prega di lavorarci sopra. Cosa intendi per nome e valore come metodo o operatore. E cosa intendi con "ex"? In che modo "And" e "IsEqualTo" sono correlati alla tua domanda? –

+0

Ok, c'è uno schema, ma qual è la domanda? – Matyas

+0

* Forse * stai cercando qualcosa come 'Expression Trees' https://msdn.microsoft.com/en-us/library/bb397951.aspx? A proposito, 'Object' non è un buon nome di classe. –

risposta

11

Per implementare modello visitatore avete bisogno di due interfacce semplici

  1. IVisitable con un metodo Accept avendo la IVisitor come parametro.
  2. IVisitor con molte Visit metodi per ogni implementazione di IVisitable

idea in modo di base del modello visitatore è quello di cambiare il comportamento in modo dinamico in base al tipo di applicazione.

Per il tuo caso, la cosa che si desidera visitare (la visitabile) è la classe Object che apparentemente non ha derivati ​​diversi e si desidera modificare il comportamento in base a un valore di proprietà non al tipo. Quindi Visitor Pattern non è quello che ti serve davvero qui e ti consiglio vivamente di considerare le risposte con il metodo ricorsivo.

Ma se si desidera veramente utilizzare il modello di visitatore qui, potrebbe sembrare qualcosa del genere.

interface IVisitable { void Accept(IVisitor visitor); } 

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

Poiché la classe Object è un semplice POCO presumo che non si vuole implementare un'interfaccia e aggiungere un metodo in questa classe. Quindi avrete bisogno di un oggetto che si adatta adapterObject al 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); 
    } 
} 

E infine l'attuazione visitatore può assomigliare a questo

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

Questo potrebbe non essere quello che vuoi. Ma un modo per creare l'output desiderato senza utilizzare il modello visitatore è aggiungere il seguente metodo alla classe Object, in questo modo:

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

Prima di tutto è necessario ordine errato nel result.Second, è Somethimes miss parentesi nel risultato.Finale dovrebbe essere:

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

Per completare questa attività è necessario utilizzare la funzione ricorsiva.

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

Di seguito il vostro esempio nel codice:

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

Il JSON rappresenta chiaramente un albero per memoria (forse prodotto da un parser) .

Il modello di visitatore utilizza il polimorfismo.

Al fine di essere utilizzate da un modello di visitatore, è necessario deserializzare esso per ottenere oggetti con la diversa Visita comportamento:

  • MethodToken
  • OperatorToken
  • NameToken

Quindi IVisitor dovrebbe implementare il metodo Visit per ciascuno:

public class Visitor 
{ 
    void Visit(MethodToken token) { /* */ } 
    void Visit(OperatorToken token) { /* */ } 
    void Visit(NameToken token) { /* */ } 
}