Visitorパターン

  1. Visitorパターン
    Compositeパターンを元に、データ構造と処理を分離する。
    抽象データ構造側で、処理を依頼するとき、acceptする。
    acceptを実装するには、visit()を要請する。


    1. 目的
      Compositeと同じ ファイル、ディレクトリ構造を処理する。
      List(一覧)処理をacceptすることで、処理を依頼する。
      ディレクトリクラスでは、Visitorに iterator を提供する必要がある。

      ListVisitorの処理で、drectoyとfileを区別するため、メソッドの引数の違い(オーバーロード)機能を利用している。

    2. ソース
      import java.util.Iterator;
      import java.util.Vector;
      
      public class Main {
          public static void main(String[] args) {
                  System.out.println("Making root entries...");
                  Directory rootdir = new Directory("root");
                  Directory bindir = new Directory("bin");
                  Directory tmpdir = new Directory("tmp");
                  Directory usrdir = new Directory("usr");
                  rootdir.add(bindir);
                  rootdir.add(tmpdir);
                  rootdir.add(usrdir);
                  bindir.add(new File("vi", 10000));
                  bindir.add(new File("latex", 20000));
                  
                  rootdir.accept(new ListVisitor());  //受け入れ
          }
      }
      
      interface Acceptor {
      
          public abstract void accept(Visitor v);
          
      }
      
      abstract class Entry implements Acceptor {
          public abstract String getName();    // 名前を得る
          public abstract int getSize();      // サイズを得る
          
          /*public Entry add(Entry entry) throws FileTreatmentException {  // エントリを追加する
              throw new FileTreatmentException();
          }
          
          public Iterator iterator() throws FileTreatmentException {  
              throw new FileTreatmentException();
          }*/
          
          public String toString() {      // 文字列表現
              return getName() + " (" + getSize() + ")";
          }
      }
      
      class File extends Entry {
          private String name;
          private int size;
          public File(String name, int size) {
              this.name = name;
              this.size = size;
          }
          public String getName() {
              return name;
          }
          public int getSize() {
              return size;
          }
          public void accept(Visitor v) {
              v.visit(this);//お出で下さい
          }
      }
      
      class Directory extends Entry {
          private String name;                    // ディレクトリの名前
          private Vector dir = new Vector();      // ディレクトリエントリの集合
          public Directory(String name) {   // コンストラクタ
              this.name = name;
          }
          public String getName() {   // 名前を得る
              return name;
          }
          public int getSize() {     // サイズを得る
              int size = 0;
              Iterator it = dir.iterator();
              while (it.hasNext()) {
                  Entry entry = (Entry)it.next();
                  size += entry.getSize();
              }
              return size;
          }
          public Entry add(Entry entry) {   // エントリの追加
              dir.add(entry);
              return this;
          }
          public Iterator iterator() {      // Iteratorの生成
              return dir.iterator();
          }
          public void accept(Visitor v) {   // 訪問者の受け入れ
              v.visit(this);//お出で下さい
          }
      }
      
      //---------------------
      abstract class Visitor {
          public abstract void visit(File file);
          public abstract void visit(Directory directory);
      }
      
      
      class ListVisitor extends Visitor {
          private String currentdir = "";     // 現在注目しているディレクトリ名
          
          public void visit(File file) {     // ファイルを訪問したときに呼ばれる
              System.out.println(currentdir + "/" + file);
          }
          
          public void visit(Directory directory) {   // ディレクトリを訪問したときに呼ばれる
              System.out.println(currentdir + "/" + directory);
              String savedir = currentdir;
              currentdir = currentdir + "/" + directory.getName();
              Iterator it = directory.iterator();
              while (it.hasNext()) {
                  Entry entry = (Entry)it.next();
                  entry.accept(this);
              }
              currentdir = savedir;
          }
      }
          
    3. 実行結果
      Making root entries...
      /root (30000)
      /root/bin (30000)
      /root/bin/vi (10000)
      /root/bin/latex (20000)
      /root/tmp (0)
      /root/usr (0)