技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

javaでのVisitorパターン

Visitorパターンは、Compositeパターンを派生させたパターンであり、入れ物・中身の構造に対して複数の処理を記述する必要がある時に適用します。
Entryクラスとは別にVisitorクラスを作成し、Visitorクラスの方に処理を記述することで、構造と処理を分けることができます。
 
【サンプルコード】
・Element.java
// Visitorの受け入れメソッドを定義するインターフェース
public interface Element {
    public abstract void accept(Visitor visitor);
}
 
・Entry.java
// 入れ物と中身を同一視するための抽象クラス
public abstract class Entry implements Element {
    // 名前を外部から参照できるようにpublicメソッドを定義
    public abstract String getName();
}
 
・Book.java
// 中身のクラス
public class Book extends Entry {

    private String name;

    public Book(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

}
 
・GameSoft.java
// 中身のクラス
public class GameSoft extends Entry {

    private String name;

    public GameSoft(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

}
 
・Bag.java
import java.util.ArrayList;
import java.util.Iterator;
// 入れ物のクラス
public class Bag extends Entry {

    private String name;
    private ArrayList<Entry> item = new ArrayList<Entry>();

    public Bag(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public Entry add(Entry entry) {
        item.add(entry);
        return this;
    }

    // Iteratorを外部から参照できるようにpublicメソッドを定義
    public Iterator<Entry> iterator() {
        return item.iterator();
    }

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

}
 
・Visitor.java
// Visitorの抽象クラス
public abstract class Visitor {
    public abstract void visit(Book book);
    public abstract void visit(GameSoft gameSoft);
    public abstract void visit(Bag bag);
}
 
・ConcreteNormalVisitor.java
import java.util.Iterator;
// 中身・入れ物に対する処理を記述するVisitorクラス
public class ConcreteNormalVisitor extends Visitor {

    public void visit(Book book) {
        System.out.println("これは" + book.getName() + "という本です。");
    }

    public void visit(GameSoft gameSoft) {
        System.out.println
            ("これは" + gameSoft.getName() + "というゲームソフトです。");
    }

    public void visit(Bag bag) {
        System.out.println("これは" + bag.getName() + "という袋です。");
        Iterator<Entry> iterator = bag.iterator();
        while (iterator.hasNext()) {
            Entry entry = (Entry) iterator.next();
            System.out.print("袋「" + bag.getName() + "」の中身:");
            entry.accept(this);
        }
    }

}
 
・ConcreteNariVisitor.java
import java.util.Iterator;
// 中身・入れ物に対する処理を記述するVisitorクラス
public class ConcreteNariVisitor extends Visitor {

    public void visit(Book book) {
        System.out.println("これは" + book.getName() + "という本ナリ。");
    }

    public void visit(GameSoft gameSoft) {
        System.out.println
            ("これは" + gameSoft.getName() + "というゲームソフトナリ。");
    }

    public void visit(Bag bag) {
        System.out.println("これは" + bag.getName() + "という袋ナリ。");
        Iterator<Entry> iterator = bag.iterator();
        while (iterator.hasNext()) {
            Entry entry = (Entry) iterator.next();
            System.out.print("袋「" + bag.getName() + "」の中身:");
            entry.accept(this);
        }
    }

}
 
・EntryMain.java
// メインクラス
public class EntryMain {

    public static void main(String[] args) {

        // 袋、本・ゲームソフトを作成
        Bag bigBag = new Bag("BigBag");
        Bag emptyBag = new Bag("EmptyBag");
        Bag bagOfBook = new Bag("BagOfBook");
        Bag bagOfSoft = new Bag("BagOfSoft");
        Book bigBook = new Book("BigBook");
        Book book1 = new Book("Book1");
        Book book2 = new Book("Book2");
        GameSoft soft1 = new GameSoft("Soft1");
        GameSoft soft2 = new GameSoft("Soft2");

        // 袋の中に詰める
        // 袋の中に詰めるのは袋でも本・ゲームソフトでも良い
        // (入れ物のクラスなのか中身のクラスなのか意識しなくて良い)
        bagOfBook.add(book1);
        bagOfBook.add(book2);
        bagOfSoft.add(soft1);
        bagOfSoft.add(soft2);
        bigBag.add(emptyBag);
        bigBag.add(bagOfBook);
        bigBag.add(bagOfSoft);
        bigBag.add(bigBook);

        // 説明を見る
        // 袋でも本・ゲームソフトでも同じように説明を見ることができる
        // (入れ物のクラスなのか中身のクラスなのか意識しなくて良い)
        // 説明の仕方もVisitorクラス毎で分けることができる
        System.out.println("[BigBag(入れ物クラス)の説明です。]");
        bigBag.accept(new ConcreteNormalVisitor());
        System.out.println();
        System.out.println("[Book1(中身クラス)の説明です。]");
        book1.accept(new ConcreteNormalVisitor());
        System.out.println();
        System.out.println("[BigBag(入れ物クラス)の説明ナリ。]");
        bigBag.accept(new ConcreteNariVisitor());
        System.out.println();
        System.out.println("[Book1(中身クラス)の説明ナリ。]");
        book1.accept(new ConcreteNariVisitor());

    }
}
 
【実行結果】
[BigBag(入れ物クラス)の説明です。]
これはBigBagという袋です。
袋「BigBag」の中身:これはEmptyBagという袋です。
袋「BigBag」の中身:これはBagOfBookという袋です。
袋「BagOfBook」の中身:これはBook1という本です。
袋「BagOfBook」の中身:これはBook2という本です。
袋「BigBag」の中身:これはBagOfSoftという袋です。
袋「BagOfSoft」の中身:これはSoft1というゲームソフトです。
袋「BagOfSoft」の中身:これはSoft2というゲームソフトです。
袋「BigBag」の中身:これはBigBookという本です。

[Book1(中身クラス)の説明です。]
これはBook1という本です。

[BigBag(入れ物クラス)の説明ナリ。]
これはBigBagという袋ナリ。
袋「BigBag」の中身:これはEmptyBagという袋ナリ。
袋「BigBag」の中身:これはBagOfBookという袋ナリ。
袋「BagOfBook」の中身:これはBook1という本ナリ。
袋「BagOfBook」の中身:これはBook2という本ナリ。
袋「BigBag」の中身:これはBagOfSoftという袋ナリ。
袋「BagOfSoft」の中身:これはSoft1というゲームソフトナリ。
袋「BagOfSoft」の中身:これはSoft2というゲームソフトナリ。
袋「BigBag」の中身:これはBigBookという本ナリ。

[Book1(中身クラス)の説明ナリ。]
これはBook1という本ナリ。