Разница между образцом посетителя и двойной отправкой

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

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

5 ответов

Решение

Короче

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

В длинных

Идея множественной отправки, по сути, заключается в том, чтобы

void fn(virtual base_a*, virtual base_b*); (примечание: не как член класса: это НЕ 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 указывает на производный_a2, а pb - на производный_b1, то будет отправлено производному_а2::fn(base_b*), независимо от того, существует ли там производный_а2:: fn (производный_b1*): тип времени выполнения указанного объекта по пб не учитывается.

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

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 указывает на производный_a1 и pb на производный_b1, в конце концов перейдем к derived_a1::on_visit(derived_b1*),

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

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

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

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

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

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

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

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

Также из Википедии:

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

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