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パターン実際に使う時の雛形にでもなればいいなと思います。