LoginSignup
2
4

More than 1 year has passed since last update.

【デザインパターン】悪戦苦闘!デザインパターン ~Visitorとダブルディスパッチ編~

Posted at

はじめに

本記事は、デザインパターン学習中の私が学習中に詰まったポイント、間違えてしまったポイントをもとに学んだことを書いたものです。
個人の理解に基づいておりますため、間違いがあれば教えていただけると幸いです。

★9ヶ月前くらいにデザインパターンの勉強のために執筆し、社内で公開していた記事をこちらにも投稿したものとなります。

Visitorパターン、難しくないですか? 

私は難しかったです。

Visitorパターンの学習中に、「ダブルディスパッチ」という言葉が出てきました。そしてどうやら、「シングルディスパッチ」や「トリプルディスパッチ」も存在するらしい。

当時はパターン自体の難しさもあって結局理解が若干あやしいままになってしまったのですが、記事化するにあたって改めて整理し直してみようと思います。

ダブルディスパッチとは?

ダブルディスパッチとは、オーバーロード(多重定義)されたメソッドについて、2個のオブジェクトが関与してどのメソッドが呼び出されるかを選択するものです。

言葉だけで説明するのも難しいため、以下のVisitorパターンのクラス図を用いて説明します。
image.png

多重定義(オーバーロード)されているのはConcreteVisitor1のVisitメソッドです。ConcreteElement1とConcreteElement2の型の引数をそれぞれ持っています。

ConcreteElement1のAcceptメソッドおよびConcreteVisitorメソッドの実装例を以下に示します。

internal class ConcreteElement1:IElement
{
	// ...(省略)

	public void Accept(IVisitor visitor)
	{
		// 自分自身をVisitorクラスに渡す
		visitor.Visit(this);
	}
}
internal class ConcreteVisitor1:IVisitor
{
	// Elementの集合(Element1・2がごっちゃに入っている想定)
	IList<IElement> m_ElementList;
	int m_CurrentIndex;

	public ConcreteVisitor1(IList<IElement> list)
	{
		m_ElementList = list;
		m_CurrentIndex = 0;
	}	

	public void Visit(ConcreteElement1 element)
	{
		Console.WriteLine("訪れたのはConcreteElement1でした。");
		list[m_CurrentIndex++].Accept(this);
	}

	public void Visit(ConcreteElement2 element)
	{
		Console.WriteLine("訪れたのはConcreteElement2でした。");
		list[m_CurrentIndex++].Accept(this);
	}
}

メソッドの呼ばれる順番としては、

①ConcreteElement1のAcceptメソッドが呼ばれる。

②Acceptメソッドで、引数で渡ってきたConcreteVisitor1のVisitメソッドを、自分自身を引数に渡して呼び出す。

③引数に渡されたElementの型がConcreteElement1であるため、Visit(ConcreteElement1 element)メソッドの処理が実行される。

④Visit(ConcreteElement1 element)メソッドで、次のElementのAcceptメソッドを、自分自身を引数に渡して呼び出す。(次のElementがConcreteElement1なのか2なのかはこの時点ではわからない。)

以降、訪れていないElementが無くなるまで繰り返し

というようになります。

VisitorはAcceptメソッドを呼んで、Elementに「自分はConcreteElement1か2か」を教えてもらうことで、2つあるVisitメソッドのどちらをを実行すれば良いかが確定します。上の順番の③の時点ですね。

このように、 呼ばれるメソッドの決定に2つのオブジェクトが関与する のがダブルディスパッチです。

ちなみに、Visitor側はConcreteVisitorを容易に増やすことができますが、Element側はConcreteElementを増やすたびにVisitor側のインタフェースおよび実装にVisitメソッドのオーバーロードを増やさなければならなくなります。

では、シングルとトリプルは?

XXディスパッチのXXはつまり、メソッドの決定に関与するオブジェクトの数です。

シングルディスパッチは、メソッドの決定に関与するオブジェクトが単一であることを示します。

Visitorパターンの例に合わせて言えば、他のElementクラスと関係なしに、ConcreteVisitor1クラス内でどのオーバーロードが呼ばれるかを決定している状態であれば、シングルディスパッチといえるのではないでしょうか。

逆に、3つのオブジェクトがメソッドの決定に関与するのが、トリプルディスパッチです。

ダブルディスパッチやトリプルディスパッチのように複数のオブジェクトが多重定義されたメソッドの決定に関与することを、多重ディスパッチと呼びます。

まとめ

呼ばれるメソッドの決定に2つのオブジェクトが関与するというところが、言葉では「フーン、そうなんだ」と理解できても、実際の設計としてはなかなか理解の難しいところであると思います。

トリプルディスパッチの例が出せていない&探しても見つからない、という状態なので、記事としては片手落ち感があります。詳しい方がいらっしゃれば教えていただけると大変助かります。

◆◆◆
最後まで読んでいただき、ありがとうございました。
以下は前回のデザインパターンの記事です。よければ合わせてどうぞ。
【デザインパターン】悪戦苦闘!デザインパターン ~State・Strategy編~
【デザインパターン】悪戦苦闘!デザインパターン ~Proxy・Flyweight編~
【デザインパターン】悪戦苦闘!デザインパターン ~Mediator・Observer編~

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4