Design Pattern | Composite Pattern(コンポジットパターン)

Compositeパターンとは?

  • Compositeという単語は混合または複合という意味である。
  • Compositeパターンは、容器と中身を同一視して再帰的な構造を作る方式である。
  • ディレクトリとファイルを一緒にディレクトリエントリとして扱うために、コンテナと中身を同じ種類のものとして扱うと便利な場合がある。例えば、容器の中には中身を入れてもよく、さらに容器を入れてもよい。このように再帰構造を作ることができる。
  • GoFデザインパターンでは、構造に関するデザインパターンに分類される。

Compositeパターンのサンプルプログラム

ディレクトリ、ファイル一覧を表示するサンプルプログラムである。

Class Diagram
Composite Pattern Class Diagram

1. Entryクラス

FileとDirectoryの基本となるクラスである。

Entry.java

package com.devkuma.designpattern.structural.composite;

public abstract class Entry {

    public abstract String getName();

    protected abstract void printList(String prefix);

    public void printList() {
        printList("");
    }
}

2. Fileクラス

ファイルを表すクラスである。

File.java

package com.devkuma.designpattern.structural.composite;

public class File extends Entry {

    private String name;

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

    @Override
    public String getName() {
        return name;
    }

    @Override
    protected void printList(String prefix) {
        System.out.println(prefix + "/" + name);
    }
}

3. Directoryクラス

ディレクトリを表すクラスである。

Directory.java

package com.devkuma.designpattern.structural.composite;

import java.util.ArrayList;
import java.util.Iterator;

public class Directory extends Entry {

    private String name;
    private ArrayList<Entry> directory = new ArrayList();

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

    @Override
    public String getName() {
        return name;
    }

    public Entry add(Entry entry) {
        directory.add(entry);
        return this;
    }

    @Override
    protected void printList(String prefix) {
        System.out.println(prefix + "/" + name);
        Iterator<Entry> it = directory.iterator();
        while (it.hasNext()) {
            Entry entry = it.next();
            entry.printList(prefix + "/" + name);
        }
    }
}

4. Mainクラス

メイン処理を実行するクラスである。

Main.java

package com.devkuma.designpattern.structural.composite;

public class Main {
    public static void main(String[] args) {

        Directory workspaceDir = new Directory("workspace");
        Directory compositeDir = new Directory("composite");
        Directory testDir1 = new Directory("test1");
        Directory testDir2 = new Directory("test2");
        workspaceDir.add(compositeDir);
        workspaceDir.add(testDir1);
        workspaceDir.add(testDir2);

        File directory = new File("Directory.java");
        File entity = new File("Entity.java");
        File file = new File("file.java");
        File main = new File("main.java");
        compositeDir.add(directory);
        compositeDir.add(entity);
        compositeDir.add(file);
        compositeDir.add(main);
        workspaceDir.printList();
    }
}

5. 実行結果

/workspace
/workspace/composite
/workspace/composite/Directory.java
/workspace/composite/Entity.java
/workspace/composite/file.java
/workspace/composite/main.java
/workspace/test1
/workspace/test2

メリット

すべてのオブジェクト(File、Directory)は共通の抽象クラスを持つため、クライアントから見たとき、それがFileなのかDirectoryなのか、中身を意識する必要がなく、同じように扱える。
また、新しいクラス(例: SymbolicLink)を追加しても、基本クラス(Entry)のインターフェースが変わらなければ、クライアントの処理には影響しない。