2015-10-04 5 views
5

Я новичок в этом шаблоне, может ли кто-нибудь помочь мне в этом?Реализация шаблона посетителя в C#

я получил объект, как это:

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

Вот пример 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": [] 
     }] 
    }] 
} 

Мой вопрос, как сделать Visitor Pattern для того, чтобы получить эту последнюю строку:

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

Ваш вопрос непонятен, пожалуйста, над ним работайте. Что вы имеете в виду, что Name и Value могут быть методом или оператором. И что вы подразумеваете под «бывшим»? Как «И» и «IsEqualTo» связаны с вашим вопросом? –

+0

Хорошо, есть схема, но в чем вопрос? – Matyas

+0

* Возможно, вы ищете что-то вроде «Деревья выражений» https://msdn.microsoft.com/en-us/library/bb397951.aspx? Кстати, 'Object' не является хорошим именем класса. –

ответ

11

Для реализации шаблона посетителя нужны два простых интерфейсов

  1. IVisitable с Accept метод с IVisitor в качестве параметра.
  2. IVisitor со многими Visit методами для каждой реализации IVisitable

Так основной идеи картины посетителя, чтобы изменить поведение динамически в зависимости от типа реализации.

Для вашего случая вещь, которую вы хотите посетить (посещаемый), является классом Object, который, по-видимому, не имеет разных производных, и вы хотите изменить поведение в соответствии со значением свойства, а не типом. Таким образом, шаблон посетителя - это не то, что вам действительно нужно здесь, и я настоятельно рекомендую вам рассмотреть ответы с помощью рекурсивного метода.

Но если вы действительно хотите использовать шаблон посетителя здесь, он может выглядеть примерно так.

interface IVisitable { void Accept(IVisitor visitor); } 

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

Поскольку Object класс является простым ПОКО я предполагаю, что вы не хотите, чтобы реализовать интерфейс и добавить метод в этом классе. Таким образом, вы будете нуждаться в adapter объекте, который адаптирует Object к 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); 
    } 
} 

И, наконец, реализация посетителя может выглядеть следующим образом

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

Возможно, это не то, что вы хотите. Но один из способов создать вывод, который вы хотите без использования шаблона визитера добавьте следующий метод к Object класса, как это:

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

Прежде всего вы имеете неправильный порядок в result.Second, somethimes вас пропущенные кронштейны в результате. Изначально это должно быть:

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

Для выполнения этой задачи вы должны использовать рекурсивную функцию.

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

Ниже ваш пример в коде:

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

JSON ясно представляет лексемы дерева (возможно произведенное анализатор) ,

Публикация шаблона использования полиморфизма.

Для того, чтобы использовать паттерном посетитель, Вы должны десериализации его, чтобы получить объекты с разной Visit поведением:

  • MethodToken
  • OperatorToken
  • NameToken

Затем IVisitor должен внедрить метод посещения для каждого:

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

 Смежные вопросы

  • Нет связанных вопросов^_^