導入
フォルダの中にファイルもあれば、さらにフォルダも入っている——そんな「木構造」を扱うとき、「これは1つのファイルか、それともフォルダか」をいちいち判定するコードは煩雑になります。Composite(コンポジット) は、「単体」と「集まり」を同じインタフェースで扱えるようにする パターンです。
説明
FileItem(単体のファイル)と Folder(複数の要素をまとめるもの)の両方が、同じ FileSystemItem インタフェースを実装します。呼び出し側は相手が単体か集まりかを気にせず、同じメソッドを呼ぶだけで済みます。
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
FileItem file1 = new FileItem("読書レポート.txt", 20);
FileItem file2 = new FileItem("写真.jpg", 500);
Folder sub = new Folder("resources");
sub.add(file2);
Folder root = new Folder("work");
root.add(file1);
root.add(sub);
System.out.println(root.getName() + ": " + root.getSize() + "KB");
}
}
interface FileSystemItem {
String getName();
int getSize();
}
class FileItem implements FileSystemItem {
private String name;
private int size;
FileItem(String name, int size) {
this.name = name;
this.size = size;
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
}
class Folder implements FileSystemItem {
private String name;
private List<FileSystemItem> children = new ArrayList<>();
Folder(String name) {
this.name = name;
}
void add(FileSystemItem item) {
children.add(item);
}
public String getName() {
return name;
}
public int getSize() {
int total = 0;
for (FileSystemItem child : children) {
total += child.getSize();
}
return total;
}
}
classDiagram
FileSystemItem <|.. FileItem
FileSystemItem <|.. Folder
Folder --> "many" FileSystemItem : children
class FileSystemItem {
<<interface>>
+getName() String
+getSize() int
}
FileItem(葉)とFolder(枝)は、どちらもFileSystemItemを実装します。FolderはList<FileSystemItem> childrenを持ち、その中身はFileItemでも別のFolderでも構いません。Folderの中にFolderを入れられるのが「木構造」たるゆえんです。Folder.getSize()は、子どもたち一人ひとりのgetSize()を呼んで合計するだけです。子がFileItemでもFolderでも同じgetSize()という呼び方で済みます。sub(resourcesフォルダ)自身もgetSize()を持つので、rootから見れば「ただの1つのFileSystemItem」として扱えます。root.getSize()はfile1(20)+sub.getSize()(file2の500)= 520KBのように、再帰的に足し合わさります。
やってみよう
下のエディタを実行し、work フォルダの合計サイズが520KBになることを確認しましょう。sub の中にさらに別の Folder を追加して、3階層にしても正しく合計されるか試してみてください。
演習
resources フォルダに 動画.mp4(1200KB)の FileItem をもう1つ追加してください。root.getSize() を表示すると合計がいくつになるか確認してください。
ヒント1を見る
FileItem file3 = new FileItem("動画.mp4", 1200); を作り、sub.add(file3); を root.add(sub); より前に呼びます。
ヒント2を見る
合計は 20(file1) + 500(file2) + 1200(file3) = 1720KBです。