контекст
Я использовал с иерархией объектов (дерево выражений) «псевдо» шаблон посетителя (псевдо, так как в нем не используется двойная диспетчеризация):
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);
}
}
Я в основном вижу два варианта:
- Сохраняйте тот же дизайн, добавляя новый метод для моей операции к каждому производному классу, за счет удобства обслуживания (не вариант, ИМХО)
- Используйте «истинный» шаблон Visitor за счет расширяемости (не вариант, так как я ожидаю, что в будущем будет реализовано больше реализаций ...), с более чем 50-ю перегрузками метода Visit, каждая из которых соответствует определенной реализации ?
Вопрос
Вы бы порекомендовали использовать шаблон Visitor? Есть ли другой шаблон, который может помочь решить эту проблему?
MyInterface
. Все ли эти классы имеют уникальную реализациюDoSomething
иDoSomethingElse
? Я не вижу, где ваш класс посетителей фактически пересекает иерархию -facade
в данный момент это больше похоже на ...Ответы:
В течение более 10 лет я использовал шаблон посетителей для представления деревьев выражений в шести крупномасштабных проектах на трех языках программирования, и я очень доволен результатом. Я нашел несколько вещей, которые сделали применение шаблона намного проще:
Не используйте перегрузки в интерфейсе посетителя
Поместите тип в имя метода, т.е. используйте
скорее, чем
Добавьте метод «поймать неизвестного» в интерфейс вашего посетителя.
Это позволило бы пользователям, которые не могут изменить ваш код:
Это позволило бы им создавать свои собственные реализации
IExpression
иIVisitor
"понимать" их выражения, используя информацию о типах во время выполнения при реализации ихVisitExpression
метода catch-all .Обеспечить реализацию
IVisitor
интерфейса по умолчанию, ничего не делающуюЭто позволило бы пользователям, которым необходимо иметь дело с подмножеством типов выражений, быстрее создавать своих посетителей и сделать свой код невосприимчивым к добавлению вами новых методов
IVisitor
. Например, написание посетителя, который собирает все имена переменных из ваших выражений, становится легкой задачей, и код не сломается, даже если вы добавите несколько новых типов выражений к себеIVisitor
позже.источник
Do not use overloads in the interface of the visitor
?