转自:http://www.techscore.com/tech/DesignPattern/Visitor.html
第13章では Visitor パターンを学びます。Visitor とは、英語で「訪問者」を意味します。Visitor パターンでは、「処理」を訪問者である Visitor オブジェクトに記述することで、処理の追加を簡単にします。処理対象となる、Acceptor オブジェクトは、Visitor オブジェクトを受け入れる accept(Visitor visitor)メソッドを実装している必要があります。
例えば、家の「水道工事」を行ってもらう場合、あなたは、「水道工事業者」を家に呼んで、「よろしくお願いします。」と言って、後は全てお 任せしますよね。同様に、「庭の手入れ」を行ってもらう場合は、「庭師」を家に呼んで、全てお任せしてしまうでしょう。そのほかにも、電気工事業者を呼ぶ ことも、リフォーム業者を呼ぶこともあるでしょう。これらの訪問者に対して、あなたは、「では、よろしく」と言って、ほとんどの作業をお任せするはずで す。
お任せの仕方に多少の違いがあるかもしれませんが、最終的には、全てを業者にお任せすることになると思います。もし、新しいサービスを提供 する業者が現れたときにも、各家庭は、なんら態度を変える必要が無く、その業者を呼んで、「よろしくお願いします。」というだけで、その新しいサービスを 受けることができます。
Visitor パターンでは、このように、受け入れる側に処理を追加することなく、処理を追加することができるパターンです。
サンプルケースでは、家庭訪問を例に考えて見ます。各家庭では、先生であろうと、近所のおばちゃんであろうと、訪問者が訪れると、知らない人でなければ、「いらっしゃい」と言って受け入れます。この際、各家庭を Acceptor 、先生を Visitor として、Visitor パターンに当てはめて考えて見ます。Visitor パターンでは、Visitor は、訪問対象となる、家庭を訪問します。訪問された家庭は、「ようこそいらっしゃいました」と先生を受けいれます。
さて、新人先生の小学校でも、秋の家庭訪問の時期がやってきました。新人先生も、初めての家庭訪問にどきどきしながら、各家庭を訪れました。
新人先生 | : | こ・こ・こ・こんにちわ |
親御さん | : | あら、いらっしゃい新人先生。 |
新人先生 | : | い・・いえ、緊張して。 |
親御さん | : | あら、先生ったらかわいい。 |
各家庭の親御さんたちは、新人先生がどきどきしているのをあざ笑うかのように冷静です。新人先生は、何で自分だけこんなにどぎまぎしているのだろうと不思議に思い、学校に帰ってベテラン先生に聞きました。
新人先生 | : | ベテラン先生、家庭訪問したとき、なぜ親御さんたちは、すごく冷静ですね。 私は、なんだかどぎまぎしちゃって。 |
ベテラン先生 | : | それは、当たり前だよ。 君は、自分の家に、誰か訪問してきたとき、そんなにどきどきするかい? |
新人先生 | : | いえ、人が訪問してくるのは別に |
ベテラン先生 | : | だよね。今回、初めての経験をしているのは、君だけで、 親御さんたちは、これまで何回も家庭訪問を受けている。 少なくとも、去年は受けているだろうし、もしかしたら、兄弟などを合わせると、 既に10回以上の家庭訪問を受けている方もおられるかも知れない。 親御さんたちは、これまで、先生方を受け入れてきたのと同じように、 君を受け入れているわけだね。 メソッドで表すと、 accept(Teacher teacher) となるだろうか。 このように、先生を受け入れるメソッドを既に持っている家庭は、 違う兄弟の家庭訪問や、違う先生の家庭訪問でも、 なんら別のメソッドを用意する必要がないわけですね。 |
新人先生 | : | なるほど。 |
さて、ではこの状況をクラスで表現してみましょう。
//先生クラス
public abstract class Teacher{
List students = null;
public abstract void visit(Home studentHome);
public abstract void visit(TanakaHome studentHome);
public abstract void visit(SuzukiHome studentHome);
public List getStudentList(){
return students;
}
}
//新人先生クラス
public class RookieTeacher extends Teacher{
List students = null;
public RookieTeacher(List students){
this.students = students;
}
public void visit(Home studentHome){
System.out.println("こんにちは");
}
public void visit(TanakaHome studentHome){
studentHome.praisedChild();
}
public void visit(SuzukiHome studentHome){
studentHome.reprovedChild();
}
}
//家庭クラス
public abstract class Home{
public abstract Object praisedChild();
public abstract Object reprovedChild();
}
//受け入れインタフェース
public interface TeacherAcceptor{
public void accept(Teacher teacher);
}
//鈴木さんの家庭
public class SuzukiHome extends Home implements TeacherAcceptor{
public Object praisedChild(){
System.out.println("あら、先生ったらご冗談を");
return new Tea();
}
public Object reprovedChild(){
System.out.println("うちの子に限ってそんなことは・・・。");
return null;
}
/**
* 訪問者を受け入れるメソッド
*/
public void accept(Teacher teacher){
teacher.visit(this);
}
}
新人先生を表す RookieTeacher クラスでは、visit メソッドがオーバーロードされており、訪問する家庭によって visit メソッドが選択されます。クラス図は、以下のようになります。
![サンプルケースクラス図](https://i-blog.csdnimg.cn/blog_migrate/3479bf4fbaab31d4f230fee096db43dd.png)
新しい先生が赴任してきた場合も、各家庭は、なんら変更を必要としませんね。
Visitor パターンの一般的なクラス図は以下のようになります。
![クラス図](https://i-blog.csdnimg.cn/blog_migrate/04977606a3223036fa12ff6fcc9d241d.png)