Разница между образцом посетителя и двойной отправкой - PullRequest
47 голосов
/ 22 марта 2012

Я читаю о шаблоне посетителя, и он выглядит так же, как Double Dispatch. Есть ли разница между ними? «Два термина» означают одно и то же.

ссылка: http://www.vincehuston.org/dp/visitor.html

Ответы [ 5 ]

41 голосов
/ 22 марта 2012

Короче говоря

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

В длинную

Идея множественной отправки - по сути - разрешить вызов, подобный

void fn(virtual base_a*, virtual base_b*); (примечание: не как классmember: это НЕ C ++!)

, который можно переопределить как

void fn(virtual derived_a1*, virtual derived_b1*);
void fn(virtual derived_a2*, virtual derived_b1*);
void fn(virtual derived_a1*, virtual derived_b2*);
void fn(virtual derived_a2*, virtual derived_b2*);

, чтобы при вызове

fn(pa, pb)

вызов перенаправлялся на переопределение, котороесоответствует фактическому типу времени выполнения как pa, так и pb.(Вы можете обобщить это на любое количество параметров)

В таких языках, как C ++, C #, Java, этот механизм не существует, и диспетчеризация типов во время выполнения в основном работает только с одним параметром (который, будучи одним, выполняется).неявно в функции, делая саму функцию членом класса:

другими словами, псевдокод

void fn(virtual base_a*, base_b*) 

становится (реальный C ++)

class base_a
{
public:
    virtual void fn(base_b*);
}

Обратите внимание, что здесь нет больше virtual перед base_b, который отныне является статическим. Вызов типа

pa->fn(pb), если pa указывает на output_a2, а pb на output_b1 будет отправленпроизводная_а2 :: fn (base_b *), независимо от того, есть ли там производный_а2 :: fn (производный_b1 *): тип времени выполнения объекта, на который указывает pb, не учитывается.

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

class base_a
{
public:
   virtual void fn(base_b*)=0;
   virtual void on_visit(derived_b1*)=0;
   virtual void on_visit(derived_b2*)=0;
};

class base_b
{
public:
   virtual void on_call(derived_a1*)=0;
   virtual void on_call(derived_a2*)=0;
};

//forward declarations, to allow pointers free use in other decls.
class derived_a1;
class derived_b1;


class derived_a1: public base_a
{
public:
   virtual void fn(base_b* pb) { pb->on_call(this); }
   virtual void on_visit(derived_b1* p1) { /* useful stuff */ }
   ...
};

class derived_b1: public base_b
{
public:
  virtual void on_call(derived_a1* pa1) { pa1->on_visit(this); }
  ... 
};

сейчас, вызов, подобный pa->fn(pb), если pa указывает на output_a1и пб к дerived_b1, наконец, перейдет к derived_a1::on_visit(derived_b1*).

13 голосов
/ 22 марта 2012

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

в C # (4.0), можно использовать ключевое слово dynamic для реализации двойной диспетчеризации, в этом случае шаблон посетителя не требуется.Вот мое решение проблемы double-dispatch с использованием ключевого слова dynamic:

5 голосов
/ 22 марта 2012

Динамическая отправка относится к концепции диспетчеризации метода, основанного на информации времени выполнения, в целом.Большинство ОО-систем (как в Java / C # / C ++) обычно реализуют динамическую диспетчеризацию с помощью методов virtual (независимо от того, являются ли все методы виртуальными, зависит от языка);это ограничивает их для отправки в соответствии с одним аргументом метода (неявная ссылка на объект).

В общем случае вы можете отправлять в соответствии с произвольным числом элементов.Например, Double Dispatch - это требование / возможность отправки в соответствии с двумя аргументами метода.

С другой стороны, Шаблон посетителя является реализацией Multi Dispatch в целом и, следовательно, Double Dispatch, в частности, в таких системах OO.

3 голосов
/ 22 марта 2012

Двойная отправка - техническая проблема, которая, в зависимости от языка, может быть решена различными способами - некоторые языки поддерживают двойную отправку напрямую.Шаблон посетителя - это шаблон, который можно использовать для решения различных задач.В случае C ++ это наиболее частое (но не единственное) решение, используемое для двойной диспетчеризации, но оно не используется исключительно для этого и может быть полезно даже в языках, которые поддерживают двойную диспетчеризацию.

1 голос
/ 22 марта 2012

Из Википедии :

шаблон посетителя имитирует двойную диспетчеризацию в традиционном объектно-ориентированном языке с одной диспетчеризацией, таком как Java, Smalltalk и C ++.

Также из Wikipedia :

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

...