Разница между посетителем pattern & Double Dispatch
Я читаю о шаблоне посетителя, и он выглядит так же, как двойная отправка. Есть ли разница между ними? Два термина означают одно и то же.
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 указывает на derived_a2, а pb на derived_b1 будет отправлен в
derived_a2::fn(base_b*), независимо от того, есть ли там derived_a2::fn (derived_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 указывает на derived_a1 и pb на derived_b1, наконец, перейдет к derived_a1::on_visit(derived_b1*)
.
шаблон Visitor составляет один решение, которое реализует поведение двойной диспетчеризации. Может быть и несколько других решений. Термин двойной диспетчеризации сам по себе не дает никакого представления о решении, на самом деле это проблема, решение которой обеспечивается шаблон Visitor.
В C# (4.0), можно использовать dynamic
ключевое слово для реализации двойной отправки, в этом случае шаблон посетителя не требуется. Вот мое решение двойной отправки
Динамическая Диспетчеризация относится к концепция диспетчеризации в метод, основанный на информации о времени выполнения, в целом. Большинство систем OO (как в Java/C#/C++) обычно реализуют динамическую отправку через virtual
методы (независимо от того, являются ли все методы виртуальными, зависят от языка); это ограничивает их отправкой в соответствии с одним аргументом метода (неявная ссылка на объект).
В общем случае вы можете захотеть отправить в соответствии с произвольным число элементов. Например, двойная отправка-это требование / возможность отправки в соответствии с двумя аргументами метода.
С другой стороны,Шаблон Visitor - это реализация Multi отправки вообще и таким образом двойной отправки в частности в таких системах OO.
двойная отправка техническая проблема, которая может, в зависимости от языка, быть решена по-разному-некоторые языки поддерживают двойную отправку напрямую. Шаблон посетитель шаблон, который может быть использован для решения различных проблем. В случае c++ это наиболее частое (но не единственное) решение, используемое для двойной отправки, но оно не используется исключительно для этого, и оно может быть полезно даже в языках, которые поддерживают двойную отправку.