web-dev-qa-db-ja.com

ビジターパターンとダブルディスパッチの違い

ビジターパターンについて読んでいますが、ダブルディスパッチと同じように見えます。 2つの間に違いはありますか。 2つの用語は同じことを意味します。

参照: http://www.vincehuston.org/dp/visitor.html

48
vamsi

要するに

それらは、ダブルディスパッチがネイティブでサポートされていない一部の言語では、マルチディスパッチサロゲートを持つために2つ(またはそれ以上)のシングルディスパッチを連結する方法としてビジターパターンにつながるという異なる概念に由来しています。

長い間

複数のディスパッチのアイデアは-基本的に-次のような呼び出しを許可します

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)
_

呼び出しは、papbの両方の実際のランタイムタイプに一致するオーバーライドにリダイレクトされます。 (これを任意の数のパラメーターに一般化できます)

C++、C#、Javaなどの言語では、このメカニズムは存在せず、ランタイム型のディスパッチは基本的に1つのパラメーターでのみ機能します(つまり、1つだけなので、関数自体をクラスのメンバーにすることで、関数内で暗黙的に行われます:

つまり、疑似コード

_void fn(virtual base_a*, base_b*) 
_

(本当のC++)になる

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

ここでは、_base_b_の前にvirtualがなくなっていることに注意してください。のような呼び出し

pa->fn(pb) paがderived_a2を指し、pbがderived_b1を指す場合は、drived_a2 :: fn(base_b *)にディスパッチされます。そこにderived_a2 :: fn(derived_b1 *)があるかどうかは関係ありません。実行pbが指すオブジェクトの-timeタイプは考慮されません。

訪問者パターンのアイデアは、別の仮想ディスパッチを呼び出す(最終的には戻る)オブジェクトの仮想ディスパッチを呼び出すことです。

_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*)に行きます。

41

訪問者パターンは、二重ディスパッチの動作を実装するoneソリューションです。他にもいくつかの解決策があります。用語double dispatch自体は、ソリューションについての考えを与えません。実際、それは訪問者パターン

C#(4.0)では、dynamicキーワードを使用して二重ディスパッチを実装できます。この場合、ビジターパターンは必要ありません。 dynamicキーワードを使用したdouble-dispatch問題の解決策は次のとおりです。

13
Nawaz

Dynamic Dispatchは、一般に、ランタイム情報に基づいてメソッドにディスパッチするconceptを指します。ほとんどのOOシステム(Java/C#/ C++など))は通常、virtualメソッドを介して動的ディスパッチを実装します(すべてのメソッドが仮想であるかどうかは言語によって異なります)。単一のメソッド引数(暗黙のオブジェクト参照)に従ってディスパッチします。

一般に、任意の数の要素に従ってディスパッチしたい場合があります。たとえば、ダブルディスパッチは、メソッドの2つの引数に従ってディスパッチするための要件/機能です。

一方、Visitor Patternは、一般的にマルチディスパッチのimplementationであり、特にこのような場合のダブルディスパッチ= OOシステム。

5
Matthieu M.

二重ディスパッチは技術的な問題であり、言語によってはさまざまな方法で解決できます。一部の言語は二重ディスパッチを直接サポートしています。ビジターパターンは、さまざまな問題を解決するために使用できるパターンです。 C++の場合、これはダブルディスパッチに使用される最も頻繁な(唯一ではない)ソリューションですが、それだけに使用されるわけではなく、ダブルディスパッチをサポートする言語でも役立つ場合があります。

3
James Kanze

Wikipedia から:

ビジターパターンは、Java、Smalltalk、C++などの従来のシングルディスパッチオブジェクト指向言語でのダブルディスパッチをシミュレートします。

他にも Wikipedia から:

上記の問題は、たとえばビジターパターンを使用して二重ディスパッチをシミュレートすることで解決できます。

1
Peter Wood