Зачем использовать шаблон посетителя?
Дубликат: Когда следует использовать шаблон дизайна посетителя
Почему кто-то хочет использовать шаблон посетителя? Я прочитал пару статей, но я ничего не получаю.
Если мне нужна функция для выставления счета, я мог бы использовать
Custom.Accept(BillVisitor)
или что-то вроде
Bill(Customer)
Второй вариант менее сложный, и функция Билла все еще отделена от класса Customer. Так почему я хотел бы использовать шаблон посетителя?
Ответы
Ответ 1
Проблема возникает, когда у вас сложная структура, т.е. иерархия или что-то еще, что не просто линейно. Когда вы не можете просто перебирать структуру, посетителю очень удобно.
Если у меня есть иерархия (или дерево), каждый Node имеет список детей. Когда я хочу применить процесс к каждому Node в дереве, приятно создать посетителя.
A Node может затем применить посетителя к себе и к каждому из его дочерних узлов. Каждый ребенок, транзитивно, делает то же самое (примените посетителя к себе, а затем к любым детям).
Это использование посетителя очень хорошо работает.
Когда у вас есть суперпростая структура данных, Visitor не добавляет большого значения.
Ответ 2
Шаблон посетителя - это взлом для языков, которые не поддерживают множественную отправку напрямую (язык, такой как С++, и Java поддерживает только одну отправку на основе объекта. Несколько диспетчеров поддерживаются CLOS). В этом случае, если вы хотите, чтобы и Bill, и Customer были полиморфными, вам придется использовать два интерфейса, BillVisitor и Customer на отдельных языках рассылки. Например: реализация accept обычно:
void accept(BillVisitor visitor) { visitor.bill(this); } // java syntax
Обратите внимание, что как счетчик # accept, так и счет BillVisitor # могут быть переопределены их соответствующими подклассами, что приводит к очень богатой комбинации поведения во время выполнения, чего нельзя добиться иначе. Это действительно надмножество того, что большинство других людей описано здесь как замена закрытия, чтобы применить функциональность к сложным структурам данных.
Ответ 3
В моем приложении CAD/CAM у меня есть Paths и Collections of Paths (PathList). Иногда мне приходится создавать коллекцию фигур. Я не только должен генерировать эту конкретную коллекцию форм, я должен включить ее в другую коллекцию фигур. Мне часто требуется дюжина или более параметров для передачи всей информации, необходимой для проведения расчетов.
Все вычисления формы (простые или сложные) в моем CAM направляют PathList, который отправляется на разные машины.
В этом дизайне лучше всего иметь некоторую настройку, которая включает в себя добавление результата в одну коллекцию.
The Path Visitor отлично подходит для этого. Каждое вычисление формы инкапсулируется в собственный класс с такими сложными свойствами, как необходимые.
Таким образом, посетитель кухонной вытяжки может иметь 8 фигур для Pathlist
В то время как посетитель кухонного счетчика добавляет 6 форм.
Ящик Visitor добавляет еще несколько.
Затем я передаю полученный PathList остальной системе, как любой другой генератор формы.
У меня легко есть варианты, добавляя другого посетителя или не запуская некоторые. Для парня, который хочет только счетчик и ящики, мне нужно всего лишь запустить двух посетителей.
Полученный код очень читабельен, что важно, когда я пересматриваю эту область на 3, 5 или 10 лет. Кроме того, из-за инкапсуляции изменения одного посетителя имеют минимальное влияние на других посетителей.
Кроме того, теперь у меня есть стандартизованный шаблон проектирования, чтобы добавить любую новую функциональность в PathList, реализовав шаблон посетителя.
Например, когда-то наш редактор свойств работал только по одному пути. Когда мы перешли на редактирование нескольких путей, было легко реализовать пользовательских посетителей для глобальных изменений ко всем путям. Это было более читаемым, а затем использовалось для циклов внутри делегатов.
Но в конечном итоге это сводится к суждению и предпочтению.
Ответ 4
Еще один приятный сюрприз для посетителей - их легко расширить, и если ваш язык позволяет это, вы можете даже использовать lamdbas для очистки вещей.
Ответ 5
В обоих случаях посетитель отделен от класса клиента. Преимущество было бы, если бы вы хотели отвлечь посетителя от класса вызывающего. Во втором случае вызывающий класс должен знать о выставлении счетов. Вместо этого у вас может быть другая процедура, которая вернет IVisitor. Затем вызывающий код может вызвать Custom.Accept(IVisitor) и не знать ничего о том, что делает посетитель.
В основном в этом случае и в случае, о котором упомянул S.Lott, вы можете думать о посетителе, как о делегате. Это функция, которую вы можете передавать как объект и использовать там, где это необходимо.