Зачем использовать шаблон посетителя?

Дубликат: Когда я должен использовать шаблон дизайна посетителя

Почему кто-то хочет использовать шаблон посетителя? Я прочитал пару статей, но я ничего не понимаю.

Если мне нужна функция для выставления счета на заказ, я мог бы использовать

Custom.Accept(BillVisitor)

или что-то вроде

Bill(Customer)

Вторая менее сложна, и функция Bill по-прежнему отделена от класса Customer. Итак, почему я хотел бы использовать образец посетителя?

5 ответов

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

Если у меня есть иерархия (или дерево), у каждого узла есть список дочерних элементов. Когда я хочу применить процесс к каждому узлу в дереве, приятно создать посетителя.

Затем узел может применить посетителя к себе и каждому из его дочерних узлов. Каждый ребенок, переходный, делает то же самое (применяет посетителя к себе, а затем к любым детям).

Такое использование Visitor работает очень хорошо.

Когда у вас есть сверхпростая структура данных, Visitor не добавляет особой ценности.

Шаблон посетителя является хаком для языков, которые не поддерживают множественную диспетчеризацию напрямую (такие языки, как C++ и Java, поддерживают только одиночную диспетчеризацию на основе объекта. Многократная диспетчеризация поддерживается CLOS). В этом случае, если вы хотите, чтобы и Билл, и Клиент были полиморфными, вам придется использовать два интерфейса, BillVisitor и Customer, на языках с одной диспетчеризацией. Например: реализация accept обычно:

void accept(BillVisitor visitor) { visitor.bill(this); } // java syntax

Обратите внимание, что как Customer # accept, так и BillVisitor # bill могут быть переопределены их соответствующими подклассами, что приводит к очень богатой комбинации поведения во время выполнения, которая не может быть достигнута иначе. Это действительно расширенный набор того, что большинство других людей описали здесь как замену замыкания для применения функциональности к сложным структурам данных.

Еще одна приятная особенность посетителей - их легко расширять, и если ваш язык позволяет это, вы даже можете использовать lamdbas для очистки.

В моем приложении CAD/CAM у меня есть Пути и Коллекции Путей (PathList). Иногда мне нужно создать коллекцию фигур. Я должен не только генерировать эту конкретную коллекцию фигур, но и включать ее в другую коллекцию фигур. Мне часто требуется дюжина или больше параметров, чтобы передать всю информацию, необходимую для расчетов.

Все вычисления формы (простые или сложные) в моей CAM направляются в PathList, который отправляется на разные машины.

С таким дизайном лучше всего иметь некоторую настройку, которая включает добавление результата в одну коллекцию.

Путь посетителя прекрасно подходит для этого. Каждое вычисление формы заключено в свой собственный класс со свойствами, настолько сложными, насколько это необходимо.

Таким образом, посетитель Кухонного капюшона может иметь 8 фигур в Pathlist

В то время как Кухонный Счетчик Посетитель добавляет 6 форм.

Ящик посетитель добавляет еще несколько.

Затем я передаю полученный PathList остальной части системы, как и любой другой генератор форм.

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

Полученный код очень удобен для чтения, что важно, когда я пересматриваю эту область через 3, 5 или 10 лет. Кроме того, из-за изменений в инкапсуляции для одного посетителя влияние на других посетителей будет минимальным.

Кроме того, теперь у меня есть стандартизированный шаблон проектирования для добавления любых новых функциональных возможностей в PathList путем реализации шаблона посетителя.

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

Но в конечном итоге все сводится к суждению и предпочтениям.

В обоих случаях посетитель отделен от класса клиента. Преимущество будет, если вы захотите абстрагировать посетителя от класса вызывающего абонента. Во втором случае вызывающий класс должен знать о биллинге. Вы могли бы вместо этого иметь другую рутину где-нибудь, чтобы вернуть IVisitor. Затем вызывающий код может вызвать Custom.Accept(IVisitor) и ничего не знать о том, что делает посетитель.

В основном, в этом случае и в случае, упомянутом С.Лоттом, вы можете думать о посетителе как о делегате. Это функция, которую вы можете передавать как объект и использовать там, где это необходимо.

Другие вопросы по тегам