Посещение пар посещаемых объектов с шаблоном Посетитель

Есть ли четкая схема, позволяющая посетителю посещать два объекта одновременно?

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

Я включил решение ниже, но считаю его беспорядочным, потому что оно требует, чтобы все посещаемые объекты включали перегрузки для всех конкретных посещаемых объектов, а также требует, чтобы посетитель включил фиктивные посещения для разрешения r-посещаемого.

Если шаблон посетителя не подходит для этого, есть ли другой шаблон, который лучше подходит?

Спасибо

using System;
using System.Diagnostics;

namespace VisitorTest
{

   class Program
   {

      static void Main(string[] args)
      {

         // Given two visitable objects...
         IVisitable stringVisitable = new StringVisitable("987");
         IVisitable numberVisitable = new NumberVisitable(123);

         // And a visitor that performs a "lVisitable + rVisitable" operation...
         PlusOpVisitor plusOpVisitor = new PlusOpVisitor();

         // Test "string + string" == "987987"
         Console.WriteLine
         (  stringVisitable.PairAccept
            (  plusOpVisitor
            ,  stringVisitable
            )
         );

         // Test "string + number" == (convert both to string) == "987123"
         Console.WriteLine
         (  stringVisitable.PairAccept
            (  plusOpVisitor
            ,  numberVisitable
            )
         );

         // Test "number + string" == (convert both to number) == #1110
         Console.WriteLine
         (  numberVisitable.PairAccept
            (  plusOpVisitor
            ,  stringVisitable
            )
         );

         // Test "number + number" == #246
         Console.WriteLine
         (  numberVisitable.PairAccept
            (  plusOpVisitor
            ,  numberVisitable
            )
         );

      }

   }

   interface IPairVisitor
   {  // Messy: Dummies, just to know the l-visitable type, while visiting the r-visitable
      IVisitable Visit(StringVisitable lVisitable, IVisitable rVisitable);
      IVisitable Visit(NumberVisitable lVisitable, IVisitable rVisitable);
      // Actual visitor operations, what to do in each case
      IVisitable Visit(StringVisitable lVisitable, StringVisitable rVisitable);
      IVisitable Visit(NumberVisitable lVisitable, StringVisitable rVisitable);
      IVisitable Visit(StringVisitable lVisitable, NumberVisitable rVisitable);
      IVisitable Visit(NumberVisitable lVisitable, NumberVisitable rVisitable);
   }

   interface IVisitable
   {  // Resolve the l-visitable, include the unresolved r-visitable
      IVisitable PairAccept(IPairVisitor visitor, IVisitable  rVisitable);
      // Messy: Resolve the r-visitable, include the previously resolve l-visitable
      // The visitable-object must know the type of all objects that it can be
      // accepted against (normal visitor pattern doesn't have this restriction)
      IVisitable PairAccept(IPairVisitor visitor, StringVisitable lVisitable);
      IVisitable PairAccept(IPairVisitor visitor, NumberVisitable lVisitable);
   }

   class PlusOpVisitor : IPairVisitor
   {  // Repeat the accept, but for the other visitable
      public IVisitable Visit
      (  StringVisitable lVisitable
      ,  IVisitable      rVisitable
      ){ return rVisitable.PairAccept(this, lVisitable);
      }
      public IVisitable Visit
      (  NumberVisitable lVisitable
      ,  IVisitable      rVisitable
      ){ return rVisitable.PairAccept(this, lVisitable);
      }
      // Perform the actual operation for each pair
      public IVisitable Visit
      (  StringVisitable lVisitable
      ,  StringVisitable rVisitable
      ){ return new StringVisitable
         (  string.Concat
            (  lVisitable.Value
            ,  rVisitable.Value
            )
         );
      }
      public IVisitable Visit
      (  StringVisitable lVisitable
      ,  NumberVisitable rVisitable
      ){ return new StringVisitable
         (  string.Concat
            (  lVisitable.Value
            ,  rVisitable.Value.ToString()
            )
         );
      }
      public IVisitable Visit
      (  NumberVisitable lVisitable
      ,  StringVisitable rVisitable
      ){ return new NumberVisitable
         (  lVisitable.Value
          + int.Parse(rVisitable.Value)
         );
      }
      public IVisitable Visit
      (  NumberVisitable lVisitable
      ,  NumberVisitable rVisitable
      ){ return new NumberVisitable
         (  lVisitable.Value
          + rVisitable.Value
         );
      }
   }

   class StringVisitable : IVisitable
   {  public StringVisitable
      (  string value
      ){ _value = value;
      }
      public string Value
      {  get { return _value; }
      }
      // Visit as an l-visitable, r-visitable still unresolved
      public IVisitable PairAccept
      (  IPairVisitor  visitor
      ,  IVisitable rVisitable
      ){ return visitor.Visit(this, rVisitable);
      }
      // Visit as an r-visitable, l-visitable resolved
      public IVisitable PairAccept
      (  IPairVisitor       visitor
      ,  StringVisitable lVisitable
      ){ return visitor.Visit(lVisitable, this);
      }
      public IVisitable PairAccept
      (  IPairVisitor       visitor
      ,  NumberVisitable lVisitable
      ){ return visitor.Visit(lVisitable, this);
      }
      public override string ToString()
      {  return string.Concat("\"", _value, "\"");
      }
      private string _value;
   }

   class NumberVisitable : IVisitable
   {  public NumberVisitable
      (  int value
      ){ _value = value;
      }
      public int Value
      {  get { return _value; }
      }
      public IVisitable PairAccept
      (  IPairVisitor  visitor
      ,  IVisitable rVisitable
      ){ return visitor.Visit(this, rVisitable);
      }
      public IVisitable PairAccept
      (  IPairVisitor       visitor
      ,  StringVisitable lVisitable
      ){ return visitor.Visit(lVisitable, this);
      }
      public IVisitable PairAccept
      (  IPairVisitor       visitor
      ,  NumberVisitable lVisitable
      ){ return visitor.Visit(lVisitable, this);
      }
      public override string ToString()
      {  return string.Concat("#", _value);
      }
      private int _value;
   }

}

person Eliott    schedule 09.04.2018    source источник
comment
Как насчет того, чтобы разместить этот вопрос на CodeReview? ТАК не о том, как улучшить рабочий код   -  person Aleks Andreev    schedule 09.04.2018


Ответы (1)


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

person Yuli Bonner    schedule 09.04.2018
comment
Спасибо. Я не уверен, что я это неправильно понимаю, но оба этих шаблона, похоже, нуждаются в некоторой форме отражения и / или определенного типа данных. Что я получаю от шаблона посетителя, так это возможность обобщить тип данных, например, мои входные данные могут быть либо String, либо Int или чем-то еще, и посетитель решает это. Это относится к шаблонам интерпретатора или деревьям выражений? - person Eliott; 09.04.2018
comment
Вам нужно построить парсер. Интерпретатор использует шаблон посетителя, но он используется для оценки выражения после того, как оно уже было проанализировано. Ваш парсер определит типы ваших операндов. Вы можете создать несколько парсеров, если вам нужно анализировать данные как разные типы. - person Yuli Bonner; 09.04.2018