Index
参考書籍
Java言語で学ぶデザインパターン入門第3版
Compositeパターンとは?
参考書籍では以下のように説明されています。
容器と中身を同一視し、再起的な構造を作るデザインパターン
メリット
- 複数個のものを集めて、あたかも1つのもののように取り扱うことができる
- 再起的構造を簡単に作ることができる
サンプルコードを見ながら、メリットを解説していきます。
サンプルコード
ファイルとディレクトリを模式的に表現したプログラムを作成していきます。
UMLはこんな感じ。
ディレクトリエントリを表す抽象クラス。
toString()メソッドではTemplateMethodパターンが使われています。
TemplateMethodパターンについては以下の記事を参照してください。
TemplateMethodパターン@Java言語で学ぶデザインパターン入門
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public abstract class Entry { public abstract String getName(); public abstract int getSize(); public void printList() { printList(""); } protected abstract void printList(String prefix); @Override public String toString() { return getName() + "(" + getSize() + ")"; } } |
ファイルを表現するクラスです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class File extends Entry{ private final String m_name; private final int m_size; public File(final String name, final int size) { m_name = name; m_size = size; } @Override public String getName() { return m_name; } @Override public int getSize() { return m_size; } @Override protected void printList(final String prefix) { System.out.println(prefix + "/" + this); } } |
ディレクトリを表すクラス。
ポイント以下の3点。
- Listの型をEntry型にしていることで、File,Directory 両方クラスのインスタンスをListに入れることができる(同一視)
- getSize()、printList()メソッドでFile,Directory両クラスのメソッドを意識して区別することなく呼べる(同一視)
- getSize()、printList()メソッドで再帰的呼び出しに対応している
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class Directory extends Entry{ private final String m_name; private List<Entry> m_directory = new ArrayList<>(); public Directory(final String name) { m_name = name; } @Override public String getName() { return m_name; } @Override public int getSize() { return m_directory.stream() .collect(Collectors.summingInt(entry -> entry.getSize())); } @Override protected void printList(final String prefix) { System.out.println(prefix + "/" + this); m_directory.stream() .forEach(entry -> entry.printList(prefix + "/" + m_name)); } //ディレクトリエントリをディレクトリに追加する public Entry add(final Entry entry) { m_directory.add(entry); return this; } } |
Mainクラス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class Main { public static void main(String[] args) { System.out.println("Making root entries..."); final Directory rootdir = new Directory("root"); final Directory bindir = new Directory("bin"); final Directory userdir = new Directory("user"); rootdir.add(bindir); rootdir.add(userdir); bindir.add(new File("vi",10000)); bindir.add(new File("latex",20000)); rootdir.printList(); System.out.println(); } } |
出力
1 2 3 4 5 6 7 |
Making root entries... /root(30000) /root/bin(30000) /root/bin/vi(10000) /root/bin/latex(20000) /root/user(0) |