Visitor パターン ( データ構造を表すクラスと, それに対する処理を行うクラスを分離する. (データ構造を表すクラスを変更することなしに処理を追加できる) )

[Visitor] = [訪問者]

このパターンは, [データ構造] と [それに体する処理] を分離することを目的とするパターンである. そのため, このパターンを適用すると, [データ構造] を変更することなしに, [新しい処理] を追加することができる.

具体的には, 訪問者である Visitor 役のオブジェクトが, 訪問先であるデータ構造要素の個々のオブジェクトを訪問し, その訪問先の公開されている資源を利用して処理を実行して回るという形になる. (データ構造役オブジェクトは, 処理を訪問者役オブジェクトに委任する)

役割

  1. Visitor (訪問者):
具体的なデータ構造の要素 (ConcreteAcceptor[A|B]) 毎に訪問して行う処理 (visit メソッド) のインタフェースを定義する. visit メソッドは, オーバーロードすることで, その訪問先要素の型に応じた処理を指定する.
  1. ConcreteVisitor[A|B] (具体的訪問者):
[Visitor] で定義したインタフェース (各 visit メソッド) を実装する.
  1. Acceptor (データ構造 (受入者)):
[Visitor] の訪問先であるデータ構造要素に, 受入口 (accept メソッド) のインタフェースを実装する.
  1. ConcreteAcceptor[A|B] (具体的データ構造 (受入者)):
[Acceptor] で定義したインタフェース (accept メソッド) を実装する.
  1. Client (利用者):
[Visitor] パターンを適用したクラスを用い処理を行う.

クラス図

Visitor パターンのクラス図

_images/designpattern-visitor012.gif

シーケンス図

Visitor パターンのシーケンス図

_images/designpattern-visitor022.gif

Visitor パターンでは, 受入者 [ConcreteAcceptor] のメソッド accept を呼び出すと, 訪問者 [ConcreteVisitor] のメソッド visit が呼び出され, 実行する処理が決定する. このような 2 重呼び出しを一般的に [ダブルディスパッチ (2 重振り分け)] と呼ぶ.

ソースコード

  1. Visitor.java
1
2
3
4
public abstract class Visitor{
    public abstract void visit(ConcreteAcceptorA acceptorA);
    public abstract void visit(ConcreteAcceptorB acceptorB);
}

2-1. ConcreteVisitorA.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class ConcreteVisitorA extends Visitor{
    private String name = "ConcreteVisitorA";

    public void visit(ConcreteAcceptorA acceptorA){
	System.out.println(name + "が" + acceptorA.getName() + "を訪問した");
    }

    public void visit(ConcreteAcceptorB acceptorB){
	System.out.println(name + "が" + acceptorB.getName() + "を訪問した");
    }
}

2-2. ConcreteVisitorB.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class ConcreteVisitorB extends Visitor{
    private String name = "ConcreteVisitorB";

    public void visit(ConcreteAcceptorA acceptorA){
	System.out.println(name + "が" + acceptorA.getName() + "に参りました");
    }

    public void visit(ConcreteAcceptorB acceptorB){
	System.out.println(name + "が" + acceptorB.getName() + "に参りました");
    }
}
  1. Acceptor.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class ConcreteAcceptorA extends Acceptor{
    private String name = "ConcreteAcceptorA";

    public String getName(){
	return name;
    }

    public void accept(Visitor visitor){
	visitor.visit(this);
    }
}

4-1. ConcreteAcceptorA.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class ConcreteAcceptorA extends Acceptor{
    private String name = "ConcreteAcceptorA";

    public String getName(){
	return name;
    }

    public void accept(Visitor visitor){
	visitor.visit(this);
    }
}

4-2. ConcreteVisitorB.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class ConcreteVisitorB extends Visitor{
    private String name = "ConcreteVisitorB";

    public void visit(ConcreteAcceptorA acceptorA){
	System.out.println(name + "が" + acceptorA.getName() + "に参りました");
    }

    public void visit(ConcreteAcceptorB acceptorB){
	System.out.println(name + "が" + acceptorB.getName() + "に参りました");
    }
}
  1. Client.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class Client{
    public static void main(String[] args){
	Visitor viA = new ConcreteVisitorA();
	Visitor viB = new ConcreteVisitorB();

	Acceptor acA = new ConcreteAcceptorA();
	Acceptor acB = new ConcreteAcceptorB();

	acA.accept(viA);
	acB.accept(viA);

	acA.accept(viB);
	acB.accept(viB);
    }
}

上記のプログラムの実行結果:

[wtopia Visitor]$ java Client
ConcreteVisitorAがConcreteAcceptorAを訪問した
ConcreteVisitorAがConcreteAcceptorBを訪問した
ConcreteVisitorBがConcreteAcceptorAに参りました
ConcreteVisitorBがConcreteAcceptorBに参りました