読者です 読者をやめる 読者になる 読者になる

totofugaのブログ

ネットワークとかc言語、perlの話。

Visitorパターン(デザインパターン)

結城さんのJava言語で学ぶデザインパターン入門を読んでみたけど、 Visitorパターンが理解できなかったので少し調べて自分なりにまとめてみた。

Visitorパターンが解決してくれる場面
Interface 動物 {
    走る();
  食べる();
}

Class 犬 : 動物 {
    走る();
  食べる();
}

class 猫 : 動物 {
    走る();
    食べる();
}

というクラスと

function 行動 ( 動物 pet ) {
  pet.走る()
  pet.食べる()
}

という動物Classを使用する関数があったとする。

この場合新しく動物を追加する場合は

class 虎 : PARENT 動物 {
    走る();
    食べる();
}

という新しいクラスを追加すればよく 通常のポリモーフィズムで簡単に実現できる。

では、行動という関数に

pet.寝る()

という行を追加したい場合はどうだろうか?

この場合はインターフェースとそれを実装するクラス すべてに寝るというメソッドを追加しなくてはならなく、 変更の分散(不吉な匂い)につながる。

これを解消してくれるのがVisitorパターンである。

Visitorパターンの例

Visitorパターンでは行動をクラスとして考える

走る run;
食べる eat;
function 行動 ( 動物 pet ) {
    run.開始(pet);
    eat.開始(pet);
}

のように呼びたいが、 このままでは開始メソッド内で、 どのペットかわからないので各ペット毎に違った動作が出来ない。 従って動物自身にそれぞれ犬開始、猫開始を呼んでもらうことで解決するようにする。

走る run;
食べる eat;
function 行動 ( 動物 pet ) {
    pet.開始(run);
    pet.開始(eat);
}

Interface 動物 {
    開始();
}

Class 犬 : PARENT 動物 {
    開始(動作 play) {
        play.犬開始(this)
    }
}

class 猫 : PARENT 動物 {
    開始(動作 play) {
        play.猫開始(this)
    }
}

Interface 動作 {
    猫開始(動物);
    犬開始(動物);
}

class 走る : 動作 {
    猫開始(動物);
    犬開始(動物);
}

class 食べる : 動作 {
     猫開始(動物);
     犬動作(動物);
}

これで新しい動作を追加する場合にクラスを一つ追加するだけで対応出きるようになり 変更の分散にならなくなる。

問題点と使いどころ

上のコードをみて気づくと思うがVisitorパターンを使用したとき、 今度は新しく虎(動物)を加えようとすると動作すべてに虎開始を追加しなくてはいけなくなり、 変更の分散につながる。

動物クラスは増えずに動作のみ増える状況 いわゆる振る舞いのみ増えるよ状況でVisitorパターンを使用することが出来る。