Composite パターンとは
「構造に関するパターン」に属する。Composite パターンを用いるとディレクトリとファイルなどのような、木構造を伴う再帰的なデータ構造を表すことができる。
コンピュータにおけるディレクトリのように、ファイルとディレクトリをまとめて扱うような場合、容器(ディレクトリ)と中身(ファイル)を同じ種類として扱うと便利な場合がある。容器の中には、中身を入れてもさらに新たな容器を入れても良く、このように入れ子になった構造、再帰的な構造を作る事ができる。
Comosite パターンとは上記のような容器と中身を同一視し、再帰的な構造を作るデザインパターンである。
example
サンプルプログラムはファイルとディレクトリを模式的に表現したもの。
- File: ファイルを表す
- MyDirectory: ディレクトリを表す(PHPではDirectoryクラスは標準で定義済みのためMyDirectoryとした)
- Entry: FileとMyDirectoryの両者をとりまとめるスーパークラス
Entry 抽象クラス
<?php abstract class Entry { abstract public function getName(): string; abstract public function getSize(): int; public function add(Entry $entry) { throw new RuntimeException(); } public function printList() { $this->printListWithPrefix(""); } abstract protected function printListWithPrefix(string $prefix); public function __toString(): string { return $this->getName() . " (" . $this->getSize() . ")"; } }
MyDirectory クラス
<?php class MyDirectory extends Entry { private $name; private $directory = []; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } public function getSize(): int { $size = 0; foreach ($this->directory as $v) { $size += $v->getSize(); } return $size; } public function add(Entry $entry): Entry { $this->directory[] = $entry; return $this; } protected function printListWithPrefix(string $prefix) { echo $prefix . "/" . $this . PHP_EOL; foreach ($this->directory as $v) { $v->printListWithPrefix($prefix . "/" . $this->name); } } }
File クラス
<?php class File extends Entry { private $name; private $size; public function __construct(string $name, int $size) { $this->name = $name; $this->size = $size; } public function getName(): string { return $this->name; } public function getSize(): int { return $this->size; } protected function printListWithPrefix(string $prefix) { echo $prefix . "/" . $this . PHP_EOL; } }
Main
<?php try { echo "Making root entries..." . PHP_EOL; $rootdir = new MyDirectory("root"); $bindir = new MyDirectory("bin"); $tmpdir = new MyDirectory("tmp"); $usrdir = new MyDirectory("usr"); $rootdir->add($bindir); $rootdir->add($tmpdir); $rootdir->add($usrdir); $bindir->add(new File("vi", 10000)); $bindir->add(new File("latex", 20000)); $rootdir->printList(); echo "" . PHP_EOL; echo "Making user entries..." . PHP_EOL; $yuki = new MyDirectory("yuki"); $hanako = new MyDirectory("hanako"); $tomura = new MyDirectory("tomura"); $usrdir->add($yuki); $usrdir->add($hanako); $usrdir->add($tomura); $yuki->add(new File("diary.html", 100)); $yuki->add(new File("Composite.java", 200)); $hanako->add(new File("memo.tex", 300)); $tomura->add(new File("game.doc", 400)); $tomura->add(new File("junk.mail", 500)); $rootdir->printList(); } catch (RuntimeException $e) { var_dump($e->getTrace()); }
実行結果
$ php Main.php Making root entries... /root (30000) /root/bin (30000) /root/bin/vi (10000) /root/bin/latex (20000) /root/tmp (0) /root/usr (0) Making user entries... /root (31500) /root/bin (30000) /root/bin/vi (10000) /root/bin/latex (20000) /root/tmp (0) /root/usr (1500) /root/usr/yuki (300) /root/usr/yuki/diary.html (100) /root/usr/yuki/Composite.java (200) /root/usr/hanako (300) /root/usr/hanako/memo.tex (300) /root/usr/tomura (900) /root/usr/tomura/game.doc (400) /root/usr/tomura/junk.mail (500)
ソースコード
役割
- Leaf役: 「中身」を表すクラス。この役の中には他のものを入れることはできない。サンプルコードにおけるFileクラス
- Composite役: 「容器」を表すクラス。Leaf役やComposite役(自身)を入れることができる。サンプルコードにおけるMyDirectoryクラス
- Component役: Leaf役とComposite役を同一視するための役。Component役は、Leaf役とComposite役に共通のスーパークラスとして実現する。サンプルコードにおけるEntryクラス
- Client役: Compositeパターンの利用者。サンプルコードにおけるMain.php
まとめ
- 再帰的構造
- 複数と単数の同一視
関連パターン
- PHP7でデザインパターン入門22/23 Commandパターン - Do Something
- PHP7でデザインパターン入門13/23 Visitorパターン - Do Something
- PHP7でデザインパターン入門12/23 Decoratorパターン - Do Something
- 作者: 結城浩
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2004/06/19
- メディア: 大型本
- 購入: 51人 クリック: 762回
- この商品を含むブログ (399件) を見る