Использование шаблона посетителя с большой иерархией объектов

Контекст

Я использую иерархию объектов (дерево выражений) как «псевдо» шаблон посетителя (псевдо, так как в нем не используется двойная отправка):

 public interface MyInterface
 {
      void Accept(SomeClass operationClass);
 }

 public class MyImpl : MyInterface 
 {
      public void Accept(SomeClass operationClass)
      {   
           operationClass.DoSomething();
           operationClass.DoSomethingElse();
           // ... and so on ...
      }
 }

Эта конструкция была, насколько это возможно, довольно удобной, так как число реализаций MyInterface значимо (~ 50 или более), и мне не нужно было добавлять дополнительные операции.

Каждая реализация уникальна (это другое выражение или оператор), а некоторые - композиты (т. е. узлы оператора, которые будут содержать другие узлы оператора /листа).

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

Но пришло время, когда мне нужно добавить новую операцию , например, довольно печатать:

 public class MyImpl : MyInterface 
 {
      // Property does not come from MyInterface
      public string SomeProperty { get; set; }

      public void Accept(SomeClass operationClass)
      {   
           operationClass.DoSomething();
           operationClass.DoSomethingElse();
           // ... and so on ...
      }

      public void Accept(SomePrettyPrinter printer)
      {
           printer.PrettyPrint(this.SomeProperty);
      }
 }    

В основном я вижу два варианта:

  • Сохраняйте тот же дизайн, добавляя новый метод для моей операции к каждому производному классу за счет ремонтопригодности (не опция, IMHO).
  • Используйте шаблон «истинный» посетителя за счет расширяемости (не вариант, поскольку я ожидаю, что на пути будет больше реализаций ...), с примерно 50+ перегрузками метода Visit, каждый из которых соответствует конкретная реализация?

Вопрос

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

10 голосов | спросил T. Fabre 6 J0000006Europe/Moscow 2012, 15:11:01

1 ответ


11

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

Не используйте перегрузки в интерфейсе посетителя

Поместите тип в имя метода, используйте

IExpressionVisitor {
    void VisitPrimitive(IPrimitiveExpression expr);
    void VisitComposite(ICompositeExpression expr);
}

, а не

IExpressionVisitor {
    void Visit(IPrimitiveExpression expr);
    void Visit(ICompositeExpression expr);
}

Добавьте к вашему интерфейсу посетителя метод «catch unknown».

Это позволит пользователям, которые не могут изменить ваш код:

IExpressionVisitor {
    void VisitPrimitive(IPrimitiveExpression expr);
    void VisitComposite(ICompositeExpression expr);
    void VisitExpression(IExpression expr);
};

Это позволит им создавать свои собственные реализации IExpression и IVisitor, который «понимает» свои выражения, используя информацию типа времени выполнения при реализации их всех-кодов VisitExpression метод.

Предоставить реализацию do-nothing по умолчанию для интерфейса IVisitor

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

ответил dasblinkenlight 6 J0000006Europe/Moscow 2012, 17:45:28

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132