2013-11-26 3 views
8

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

  • «Удобный» и «элегантный», кажется, означает много и много шаблонного кода
  • Поэтому код трудно следовать. Также «accept»/«visit» не очень описателен
  • Даже уродливый шаблонный код, если ваш язык программирования не имеет перегрузки метода (например, Vala)
  • Вы не можете вообще добавлять новые операции к существующей иерархии типов без изменения всех классы, так как вам нужны новые методы 'accept'/'visit' везде, как только вам понадобится операция с различными параметрами и/или возвращаемое значение (изменения в классах по всему месту - это одна вещь, которую должен был избегать этот шаблон дизайна !?)
  • Добавление нового типа в иерархию типов требует изменений в всех посетителей. Кроме того, ваши посетители не могут просто игнорировать тип - вам нужно создать пустой метод посещения (шаблонный снова)

Это все только кажется, что очень много работы, когда все, что вам нужно сделать, это на самом деле:

Единственное реальное преимущество, которое я вижу (что, кстати, я не видел нигде): шаблон посетителя, вероятно, самый быстрый способ реализовать вышеприведенный фрагмент кода с точки зрения времени процессора (если у вас нет язык с двойной отправкой или эффективное сравнение типов в виде псевдокода выше).

Вопросы:

  • Итак, какие преимущества шаблона посетителя я пропустил?
  • Какие альтернативные концепции/структуры данных могут быть использованы для обеспечения того, чтобы приведенный выше примерный образец кода выполнялся одинаково быстро?
+0

ИМО вы пропустили важный момент ... вы должны ** НИКОГДА ** (I будет писать еще больше) имеют что-то вроде 'switch (type of obj)'. Не имеет значения, посетитель или нет. Если ваш код не такой, вы не будете писать код шаблона. –

+0

** Примеры **, когда посетитель может вам помочь? Представьте, что нужно писать утилиту для поиска текста внутри файлов. Поисковая система будет посещать каждый элемент файловой системы (файлы будут посещаться, а каталоги будут распространять посещение каждого дочернего элемента). Элементы файловой системы могут быть каталогами, файлами, ссылками на FTP-сайты ... поисковая система никогда не узнает, с чем это работает. Посетитель не должен знать точный тип (не из-за шаблона посетителя, а из-за принципов ООП ...) –

+0

Посетители - это слишком низкий уровень. Нет смысла использовать посетителей, когда вы можете сделать что-то вроде этого: http://www.cs.indiana.edu/~dyb/pubs/nano-jfp.pdf –

ответ

3

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

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

В принципе, преимущество в том, что оно позволяет вам выбрать правильный метод для вызова во время выполнения, а не во время компиляции, - и доступные методы на самом деле расширяемы.

Для получения дополнительной информации, посмотрите на эту статью - http://rgomes-info.blogspot.co.uk/2013/01/a-better-implementation-of-visitor.html

4

По опыту, я бы сказал, что «Добавление нового типа в иерархии типов требует изменения всех посетителей» является преимуществом. Потому что это определенно заставляет вас рассматривать новый тип, добавленный во ВСЕХ местах, где вы делали некоторые специфичные для конкретного типа вещи. Это мешает вам забыть ...

+0

Это не всегда требуется, и, во-вторых, это полностью противоречит основным принципам Open/Closed. –

2

Для насколько я видел до сих пор существует два варианта использования/выгоды для шаблона дизайна посетитель:

  1. Double диспетчерский
  2. Отдельные структуры данных от операций по ним

Двойная отправка

Предположим, у вас есть класс Vehicle и класс VehicleWasher. VehicleWasher имеет метод Wash (Vehicle):

VehicleWasher 
    Wash(Vehicle) 

Vehicle 

Кроме того, мы также имеем специальные транспортные средства, как автомобиль, так и в будущем мы будем также иметь другие специальные транспортные средства. Для этого у нас есть класс автомобилей, но и определенный класс CarWasher, который имеет работу, специфичную для стиральных машин (псевдо код):

CarWasher : VehicleWasher 
    Wash(Car) 

Car : Vehicle 

Тогда рассмотрит следующий код клиента постирать конкретное транспортное средство (обратите внимание, что х и шайбу объявляются с использованием их базового типа, так как экземпляры могут быть динамически создается на основе пользовательского ввода или внешних конфигурационных значений, в данном примере они просто созданы с новым оператором, хотя):

Vehicle x = new Car(); 
VehicleWasher washer = new CarWasher(); 

washer.Wash(x); 

Многие языки используют единую отправку для вызова соответствующая функция. Единая отправка означает, что во время выполнения только одно значение учитывается при определении способа вызова. Поэтому мы будем рассматривать только настоящий тип шайбы. Фактический тип x не учитывается. Поэтому последняя строка кода будет ссылаться на CarWasher.Wash (Vehicle) и NOT CarWasher.Wash (Car).

Если вы используете язык, который не поддерживает множественную отправку, и вам это нужно (я могу с полным основанием сказать, что я никогда не сталкивался с такой ситуацией), тогда вы можете использовать шаблон дизайна посетителя, чтобы включить это. Для этого нужно сделать две вещи. Прежде всего добавить метод Accept к классу транспортных средств (далее посещаемый), который принимает VehicleWasher в качестве гостя, а затем вызвать его работу Wash:

Accept(VehicleWasher washer) 
    washer.Wash(this); 

Второй вещь, чтобы изменить код вызова и заменить шайбу. Мытье (х); линия со следующим:

x.Accept(washer); 

Теперь для вызова метода Accept фактического типа х считается (и только то, что х, так как мы предполагаем, чтобы использовать единый язык отправки). При реализации метода Accept метод Wash вызывается на объекте шайбы (посетителя). Для этого рассматривается фактический тип шайбы, и это вызовет CarWasher.Wash (Car). Объединив два отдельных отправления, выполняется двойная отправка.

Теперь, чтобы рассказать о своем замечании таких терминов, как Accept and Visit и Visitor, очень неспецифические. Это абсолютно верно. Но это по какой-то причине.

В этом примере рассмотрите требование о внедрении нового класса, способного ремонтировать транспортные средства: автомобильRepairer. Этот класс можно использовать только в качестве посетителя в этом примере, если он наследует от VehicleWasher и имеет свою логику восстановления внутри метода Wash. Но это, конечно, не имеет никакого смысла и будет путать.Поэтому я полностью согласен с тем, что шаблоны проектирования имеют тенденцию иметь очень неопределенное и неспецифическое наименование, но это делает их применимыми ко многим ситуациям. Чем конкретнее ваше именование, тем более ограничительным может быть.

В вашем заявлении о коммутаторе учитывается только один тип, который на самом деле является ручным способом отправки. Применение шаблона дизайна посетителя вышеописанным способом обеспечит двойную отправку. Таким образом, при добавлении дополнительных типов в вашу иерархию необязательные дополнительные методы посещения. Конечно, это добавляет некоторую сложность, поскольку делает код менее удобочитаемым. Но, конечно, все шаблоны приходят по цене.

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

Альтернативой является использование языка, который поддерживает множественную отправку. Например, .NET не поддерживал его до версии 4.0, которая ввела динамическое ключевое слово. Тогда в C# вы можете сделать следующее:

washer.Wash((dynamic)x); 

Поскольку х затем преобразуется в динамический тип его фактический тип будет рассматриваться для отправки и поэтому будут использоваться оба х и стеклоомыватели, чтобы выбрать правильный метод так, чтобы CarWasher.Wash (Car) будет вызван (что сделает код корректным и останется интуитивным).

Отдельные структуры данных и операции

Другая выгода и требование заключается в том, что он может отделить от структуры данных операций. Это может быть преимуществом, поскольку оно позволяет добавлять новых посетителей, которые имеют собственные операции, а также позволяют добавлять структуры данных, которые «наследуют» эти операции. Однако он может применяться только в том случае, если это разделение может быть сделано/имеет смысл. Классы, которые выполняют операции (посетители), не знают структуру структур данных и не должны знать, что делает код более удобным и многоразовым. При применении по этой причине посетители имеют операции для разных элементов в структурах данных.

Скажем, у вас разные структуры данных, и все они состоят из элементов класса Item. Структуры могут быть списки, стеки, деревья, очереди и т.д.

Вы можете реализовать посетителей, что в этом случае будет иметь следующий метод:

Visit(Item) 

Структуры данных должны принимать посетителей, а затем вызвать Метод посещения для каждого элемента.

Таким образом вы можете реализовать все виды посетителей, и вы можете добавлять новые структуры данных, если они состоят из элементов типа Item.

Для более конкретных структур данных с дополнительными элементами (например, узла) вы можете рассмотреть конкретного посетителя (NodeVisitor), который наследуется от вашего обычного посетителя, и ваши новые структуры данных принимают этого посетителя (Accept (NodeVisitor)). Новые посетители могут использоваться для новых структур данных, а также для старых структур данных из-за наследования, и поэтому вам не нужно изменять существующий «интерфейс» (в этом случае суперкласс).

3

Это старый вопрос, но я бы хотел ответить.

Шаблон посетителя полезен, главным образом, когда у вас есть составной шаблон, в котором вы строите дерево объектов, и такое расположение деревьев непредсказуемо.

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

Посетитель может также нести объект, который что-то делает на каждом узле, который он может найти на этом дереве. этот посетитель может быть составным, который связывает множество операций над каждым узлом или может переносить объект-посредник для опосредования операций или отправки событий на каждом узле.

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

Смежные вопросы