デザインパターンを勉強してみる--第3回 Compositeパターン--


Compositeパターンとは

Composite パターンを学びます。

みなさん、PC上のフォルダとそのフォルダの中身を消したい時ってどうしますか?

フォルダごと消しますよね?

フォルダ開いてファイル1つずつ消したりしないですよね?

Compositeパターンは、入れ物と中身に対して同じことをしたい時に使うパターンです。

サンプルケース

以下の4つのソースではCompositeパターンを用いてディレクトリ削除を表現しました。

ソースを追いかけてみてください。どの辺がCompositeパターンなのか分かりますか?

・PCの中のもの(ファイル、ディレクトリetc..)

ThingsInPC.java
public interface ThingsInPC {
  public void remove();
}

・ファイル

File.java
public class File implements ThingsInPC {
  private String name = null;

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

  public void remove(){
    System.out.println(name + "を削除しました");
  }
}

・ディレクトリ

Directory.java
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

public class Directory implements ThingsInPC {
    private List<ThingsInPC> list = null;
    private String name = null;
    public Directory(String name){
        this.name = name;
        list = new ArrayList<ThingsInPC>();
    }
    public void add(ThingsInPC entry){
        list.add(entry);
    }
    public void remove(){
        Iterator<ThingsInPC> itr = list.iterator();
        while(itr.hasNext()){
            ThingsInPC entry = itr.next();
            entry.remove();
        }
        System.out.println(name + "を削除しました。");
    }
}

・メイン処理:ディレクトリごと削除

Main.java
public class Main {
    public static void main(String args[]){
      Directory dir = new Directory("dir");
      File dirFile1 = new File("dirFile1");
      File dirFile2 = new File("dirFile2");
      Directory subDir = new Directory("subDir");
      File subDirFile1 = new File("subDirFile1");
      File subDirFile2 = new File("subDirFile2");

      // サブディレクトリへのファイル配置
      subDir.add(subDirFile1);
      subDir.add(subDirFile2);

      // ディレクトリへのファイル&サブディレクトリ配置
      dir.add(dirFile1);
      dir.add(subDir);
      dir.add(dirFile2);

      // ディレクトリを中身ごと削除
      dir.remove();
    }
}

FileクラスとDirectoryクラスをみるとThingsInPCインターフェースを実装して
どちらも”PCの中のもの”として「同一視」しています。

その上で、Mainクラスの最終行でdir.removeが実行されるとどうなるか。

    public void remove(){
        Iterator<ThingsInPC> itr = list.iterator();
        while(itr.hasNext()){
            ThingsInPC entry = itr.next();
            entry.remove();
        }
        System.out.println(name + "を削除しました。");
    }

まぁ、while文の中で、itrに格納されたdirFile1やらsubDirやらにremoveをかけていきます。
で、entryにsubDirが入った状態でentry.removeが呼び出されると
subDirのDirectory.removeが呼び出され、subDir内のsubDirFile1、subDirFile2の
File.removeを実行するわけですね。

要するに、ディレクトリとその直下のファイル、およびサブディレクトリとその直下も含めて
マルッと消していくわけです。

これは先程言ったThingsInPCによる「同一視」をしていないと実現できません。

まとめ

Compositeパターンは入れ物と中身を同一視します。
その上で入れ物と中身に順繰りに同じ処理をしていきます。
入れ物と中身、中身の中身、中身の中身の・・というように。
このように、階層構造をもつものに対して力を発揮するパターンと言えます。

※ちなみに、階層構造を持つものに対し、
どんどん下層に下りながら同じ処理を実行していくことを「再帰的」といいます。
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1364814303

次回

MediatorとObserverのどちらか。

参考資料