C++と色々

主にC++やプログラムに関する記事を投稿します。

Visitorパターン

Visitorパターンはクラスを、データクラス(Acceptor)と処理クラス(Visitor)に分離し、データクラスを変更せずに新しい操作を定義できるようにするパターンです。

  • データクラスが異なるいくつかの要素を持ち、それらの型ごとに異なる操作が必要な時
  • データクラスから継承した派生クラスごとに異なる操作が必要な時
  • あるクラスを変更できない事情がある時(複数人が同時にクラスを変更したい時や、外のライブラリなど)に、そのクラス自体を変更せず機能を追加したい時
  • 総じて頻繁に処理の追加をする時

に使用出来ます。また、クラス自体に処理を定義した時と違い、クラスの修正は必要なく新しい処理を追加することができます。これは開放/閉鎖原則を守っていることにもなります。VisitorパターンはBoost.Variantなどで使用されています。

Visitorパターンによってクラスを変更せず、機能追加が容易になるというメリットがありますが、以下の様なデメリットがあります。

  • Visitor未使用時(普通のデータとそれに対する処理が纏まったクラス)と比べてコードが複雑になる
  • 再帰やイテレータを用いたデータ構造と処理の分離ができなくなる

なので、Visitorクラスが少数の時の時は設計が必要以上に複雑になる為、Visitorパターンを使うべきではありません。実際Visitorパターンを使うことは稀だと思います。
イメージ

#include <iostream>
using std::cout;
using std::endl;

class visitor;

class element
{
public:
	virtual void accept(visitor& v) = 0;
	virtual void disp() const = 0;
};

class element_a
	: public element
{
public:
	virtual void accept(visitor& v) override;
	virtual void disp() const override;
};

class element_b
	: public element
{
public:
	virtual void accept(visitor& v) override;
	virtual void disp() const override;
};

class visitor
{
public:
	virtual void visit(element_a& e) const = 0;
	virtual void visit(element_b& e) const = 0;
};

class visitor_a
	: public visitor
{
public:
	virtual void visit(element_a& e) const override;
	virtual void visit(element_b& e) const override;
};

class visitor_b
	: public visitor
{
public:
	virtual void visit(element_a& e) const override;
	virtual void visit(element_b& e) const override;
};

void element_a::accept(visitor& v)
{
	v.visit(*this);
}

void element_a::disp() const
{
	cout << "element_a" << endl;
}

void element_b::accept(visitor& v)
{
	v.visit(*this);
}

void element_b::disp() const
{
	cout << "element_b" << endl;
}

void visitor_a::visit(element_a& e) const
{
	cout << "visitor_a" << endl;
	e.disp();
}

void visitor_a::visit(element_b& e) const
{
	cout << "visitor_a" << endl;
	e.disp();
}

void visitor_b::visit(element_a& e) const
{
	cout << "visitor_b" << endl;
	e.disp();
}

void visitor_b::visit(element_b& e) const
{
	cout << "visitor_b" << endl;
	e.disp();
}

int main()
{
	element_a ea;
	element_b eb;

	ea.accept(visitor_a());
	cout << "-------------------" << endl;
	eb.accept(visitor_a());
	cout << "-------------------" << endl;
	ea.accept(visitor_b());
	cout << "-------------------" << endl;
	eb.accept(visitor_b());
	cout << "-------------------" << endl;
}

実行結果

visitor_a
element_a
-------------------
visitor_a
element_b
-------------------
visitor_b
element_a
-------------------
visitor_b
element_b
-------------------
続行するには何かキーを押してください . . .

無駄に長いですし、このコード自体に特に意味は無いですが、Visitorパターン実際に使う時の雛形にでもなればいいなと思います。