Composite パターン

Composite パターン（コンポジット・パターン）とは、GoF (Gang of Four; 4人のギャングたち) によって定義された
デザインパターンの1つである。「構造に関するパターン」に属する。Composite パターンを用いるとディレクトリとファイルなどのような、木構造を伴う再帰的なデータ構造を表すことができる。

Composite パターンにおいて登場するオブジェクトは、「枝」と「葉」であり、これらは共通のインターフェースを実装している。そのため、枝と葉を同様に扱えるというメリットがある。

 クラス図 
Composite パターンのクラス図を以下に挙げる。


 利用例 
Composite パターンを用いてディレクトリ構造を表すJavaプログラムの例を示す。
このプログラムは、
枝を表すクラス : Folderクラス
葉を表すクラス : Fileクラス
共通インタフェース : FileInterfaceインタフェース
実行例を示すためのクラス : DirectoryUserクラス
から構成される。

 import java.util.ArrayList;
 import java.util.List;

 interface FileInterface {
 	public void defaultMethod(int depth);
 	public List<FileInterface> getChildren();
 	public boolean addComponent(FileInterface c);
 	public boolean removeComponent(FileInterface c);
 }


 class File implements FileInterface {

 	private String name;

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

 	public void defaultMethod(int depth) {
 		for (int i = 0; i < depth; i++) System.out.print("  ");
 		System.out.println("file:" + this.name);
 	}

 	public List<FileInterface> getChildren() { return null; }
 	public boolean addComponent(FileInterface c) { return false; }
 	public boolean removeComponent(FileInterface c) { return false; }

 }


 class Folder implements FileInterface {
 	private String name;
 	private List<FileInterface> fileList = new ArrayList<FileInterface>();
 	public Folder(String name) { this.name = name; }

 	public void defaultMethod(int depth) {
 		for (int i = 0; i < depth; i++)
 			System.out.print("  ");
 		System.out.println("folder:" + name);
 		for (FileInterface file : fileList) {
  			file.defaultMethod(depth + 1);
 		}
 	}

 	public List<FileInterface> getChildren() { return this.fileList; }
 	public boolean addComponent(FileInterface c) { return this.fileList.add(c); }
 	public boolean removeComponent(FileInterface c) { return this.fileList.remove(c); }
 }


 public class DirectoryUser {
 	public static void main(String [] args){
 		FileInterface root = new Folder("root");
 		FileInterface usr = new Folder("usr");
 		FileInterface var = new Folder("var");
 		FileInterface home = new Folder("home");
 		FileInterface user1 = new Folder("user1");
  		FileInterface file1 = new File("file1");

 		root.addComponent(usr);
 		usr.addComponent(var);
 		root.addComponent(home); 
 		home.addComponent(user1);
 		user1.addComponent(file1);
 		root.defaultMethod(0);

 	}
 }
実行結果
folder:root
  folder:usr
    folder:var
  folder:home
    folder:user1
      file:file1

 注意事項 
Composite パターンを用いる際には、データ構造がきちんと木構造を保つようにしなければならない。
親子関係が循環してしまった場合、Component#operation()を実行した際に無限ループに陥るからである。

例えば、利用例のソースコードを書き換えた以下のプログラムは、処理が

dir1.defaultMethod(0);

の行に達した時に、無限に出力し続けてしまう。

 public class LoopExample {
 	public static void main(String [] args){
 		FileInterface dir1 = new Folder("dir1");
 		FileInterface dir2 = new Folder("dir2");
 		FileInterface dir3 = new Folder("dir3");

 		dir1.addComponent(dir2);
 		dir2.addComponent(dir3);
 		dir3.addComponent(dir1); 
 		dir1.defaultMethod(0);
 	}
 }

 関係するパターン 
Interpreter パターン  文法表現が Composite パターンとなる場合が多い。

 関連項目 
木構造 (データ構造)
再帰データ型
再帰呼び出し
無限ループ

Category:デザインパターン (ソフトウェア)