Хорошая причина использовать шаблон дизайна Visitor? - PullRequest
5 голосов
/ 10 сентября 2010

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

Я понимаю, как все работает. Классический пример животное, собака, кошка всегда работает как шарм.

Дело в том, что этот код

int main()
{
    Cat c;
    Sound theSound;
    c.letsDo(&theSound);
}

кажется мне таким неестественным. Почему?

Я имею в виду, да, таким образом, у меня есть мои модели собак и кошек недифференцированные (впервые я использую это слово на английском языке, кстати), потому что настоящее значение скрыто в классе Sound, но это просто способ утяжелить твой код? Разве полиморфизма недостаточно, чтобы сделать что-то подобное?

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

Ответы [ 4 ]

8 голосов
/ 10 сентября 2010

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

Рассмотрим классический пример использования шаблона посетителя, а именно операцию над узлами некоторого абстрактного синтаксиса дерева.Чтобы добавить некоторые детали, скажем, вы только что написали библиотеку синтаксического анализатора для SQL, которая принимает строки, анализирует их и возвращает AST для материала, найденного во входных данных.Если вы не можете предвидеть все возможные варианты использования вашего клиентского кода для такого AST, вы должны предоставить «общий» способ обхода AST.Предоставление DOM-подобных функций доступа (getNodeType, getParentNode, getPreviousNode) - это один из способов.Проблема здесь в том, что это ложится тяжелым бременем на клиентов вашей библиотеки, потому что они должны сами выполнять диспетчеризацию.Более того, они должны знать очень подробно, какие указатели должны следовать для каждого возможного типа узла:

void 
walk_tree(AstNode* node) 
{
    switch( node->getNodeType() ) {
    case SELECT_NODE:
        for( AstNode* child = node->getFirstChild(); child; child = child->getNextNode() ) {
             walk_tree(child);
        }
        break;
    ...
    }
}

Шаблон посетителя переносит эту нагрузку с клиента в библиотеку.

3 голосов
/ 13 сентября 2010

Допустим, у вас есть некоторые базовые вещи , определенные в библиотеке, которой вы не владеете, и вам нужно ее расширить. Как:

// In base lib:
interface ISomething {
    void DoSomething();
}

class Something1 : ISomething {
    // ...
}

class Something2 : ISomething {
    // ...
}

Полиморфизм позволяет вам определять новые вещи, с которыми вы можете выполнять операции:

// In your lib:
class MySomething : ISomething {
}

И теперь базовая библиотека может работать с вашим MySomething, как если бы она его определила. не позволяет вам добавлять новые операции . DoSomething - единственное, что мы можем сделать с ISomething. Шаблон Visitor обращается к этому.

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

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

2 голосов
/ 30 января 2011

Шаблон посетителя очень полезен.

Существует как минимум три основных причины для его использования:

  1. Сокращение распространения кода, который незначительно отличается при изменении структуры данных.

  2. Применить одно и то же вычисление к нескольким структурам данных, не изменяя код, который реализует вычисление.

  3. Добавление информации в устаревшие библиотеки без изменения устаревшего кода.

Пожалуйста, посмотрите на статью, которую я написал об этом .

Приветствия

1 голос
/ 17 октября 2010

Я использовал шаблон посетителя, когда у меня было дерево объектов, и мне нужно было напечатать содержимое различными способами.Запятая, XML, что угодно.Вместо того, чтобы добавлять в класс дерева новый метод печати для каждого выходного формата, я использовал шаблон посетителя и создал классы CommaSepVisitor, XMLVisitor и HTMLVisitor.Код дерева никогда не менялся, так как я добавлял больше типов посетителей, поэтому я никогда не вводил ошибок.Сами посетители написали легко.

...