Composite パターン

  1. Compositeパターン
    compositeパターンは再帰的に定義されたオブジェクトを扱います。
     ディレクトリにはディレクトリとファイルが含まれる
     ディレクトリのサイズはその中に含まれるディレクトリとファイルのサイズの合計である。
    この場合、ディレクトリとファイルを統合する抽象クラス:Entryを定義し、Entryの抽象メソッドをディレクトリとファイルの各クラスで定義します。
     こうすると、「ディレクトリのサイズはディレクトリに含まれるEntryのサイズの合計」と定義できます。統合クラスを仲介しないと、ディレクトリのサイズを合計するとき、ディレクトリとファイルを区別する必要がありますが。Entryクラスを利用すると、Entryサイズを具体化するとき、ディレクトリとファイルのサイズに自動的に振り分けてくれます。
     具体的には、ディレクトリのサイズは、そこに含まれる Entry の合計としてされます。Entryがファイルかディレクトリかにより、自動的に対応する getSize() が利用されます。
        public int getSize() {   // サイズを得る
            int size = 0;
            Iterator it = directory.iterator();
            while (it.hasNext()) {
                Entry entry = (Entry)it.next();
                size += entry.getSize();
            }
            return size;
        }


    1. 目的

      ディレクトリへの追加、および、一覧表示(サイズ付)を行います。ディレクトリに追加する項目は、スーパークラスであるEntryとして追加しています。
       ディレクトリのprintList()は、ディレクトリで定義された printList(String prefix) ではなく、Entry クラスのprintList()となり、これが、仮想メソッドの printList(String prefix) を呼び出しています。Entry クラスの add() はここでは未定義としました。

       仮想クラスとは無関係ですが、最初 System.out.println(prefix + "/" + this); のthisの表現にびっくりしました。これは、this.toString() と同じ意味です。クラスに toString() を定義しておくと、そのクラスを「そのまま」表示できます。

    2. ソース
      import java.util.*;
      
      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");
                  rootdir.add(bindir);
                  //rootdir.add(tmpdir);
                  bindir.add(new File("vi", 10000));
                  bindir.add(new File("latex", 20000));
                  rootdir.printList();
      
          }
      }
      
      public abstract class Entry {
          public abstract String getName();   // 名前を得る
      
          public abstract int getSize();  // サイズを得る
      
          /*public Entry add(Entry entry)  {   // エントリを追加する
          }*/
      
          public void printList() {   // 一覧を表示する
              printList("");
          }
          protected abstract void printList(String prefix); // 一覧を表示する
      
          public String toString() {      // 文字列表現
              return getName() + " (" + getSize() + ")";
          }
      }
      
      public 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;
          }
          protected void printList(String prefix) {
              System.out.println(prefix + "/" + this.toString());
          }
      }
      
      
      public class Directory extends Entry {
          private String name;                    // ディレクトリの名前
          private Vector directory = new Vector();      // ディレクトリエントリの集合
          public Directory(String name) {         // コンストラクタ
              this.name = name;
          }
      
          public String getName() {               // 名前を得る
              return name;
          }
      
          public int getSize() {                  // サイズを得る
              int size = 0;
              Iterator it = directory.iterator();
              while (it.hasNext()) {
                  Entry entry = (Entry)it.next();
                  size += entry.getSize();
              }
              return size;
          }
      
          public Entry add(Entry entry) {         // エントリの追加
              directory.add(entry);
              return this;
          }
      
          protected void printList(String prefix) {       // エントリの一覧
              System.out.println(prefix + "/" + this.toString());
              Iterator it = directory.iterator();
              while (it.hasNext()) {
                  Entry entry = (Entry)it.next();
                  entry.printList(prefix + "/" + name);
              }
          }
      }

    3. 実行結果

      /root (30000)
      /root/bin (30000)
      /root/bin/vi (10000)
      /root/bin/latex (20000)