最終更新日: 20230305
はじめに
ライトな感じでコンポジットパターンに触れてみようってテーマの記事です
コンポジットパターンってようは、ツリー型データ構造作りたいから同一視して再帰処理するってことだと思うんですけど
まず頭に入れるべきことは「同一視」かなって個人的には思ってます
ツリー型データ構造が必要ならライブラリ使えば良いで終わりな気がするけど
同一視って設計概念は頭の片隅に置いとけば役に立つ日が来る予感!
何が実現できる?
「容器」と「中身」を同一視するということは
「容器」を「中身」として見ることができるので
下記のような「容器」に「容器」を格納する入れ子構造が実現可能
* ディレクトリ
* ディレクトリA
* ファイルA
* ディレクトリB
* ファイルB
* ファイルC
クラス図
引用: Java言語で学ぶデザインパターン入門 結城 浩
ソース
使用する言語はJavaです
引用: Java言語で学ぶデザインパターン入門 結城 浩
Entryクラス
この柚象クラスを親(スーパークラス)に子(サブクラス)を作ることで
Entry型として同一視することができる
public abstract class Entry {
public abstract String getName();
public abstract int getSize();
public Entry add(Entry entry) throws FileTreatmentException {
throw new FileTreatmentException();
}
public void printList() { // Fileクラスの実装例で解説すると System.out.println(prefix + "/" + this) の prefixが "" ってこと
printList("");
}
protected abstract void printList(String prefix);
public String toString() {
return getName() + " (" + getSize() + ")";
}
}
Fileクラス
Entryのサブクラス
public class File extends Entry {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
protected void printList(String prefix) {
System.out.println(prefix + "/" + this);
}
}
Directoryクラス
重要なのはadd関数かな
DirectoryとFileはEntryクラスのサブクラスだから両方Entry型として扱える
だからDirectoryという「容器」に「容器と中身」を入れることができる
printList
やgetSize
も同一視してるから再帰的に処理できるねってことです
import java.util.ArrayList;
import java.util.Iterator;
public class Directory extends Entry {
private String name;
private ArrayList directory = new ArrayList();
public Directory(String name) {
this.name = name;
}
public String getName() {
return name;
}
public int getSize() {
int size = 0;
Iterator it = directory.iterator();
while (it.hasNext()) {
Entry entry = (Entry) it.next();
size += entry.getSize();
}
return size;
}
public Entry add(Entry entry) {
directory.add(entry);
return this;
}
protected void printList(String prefix) {
System.out.println(prefix + "/" + this);
Iterator it = directory.iterator();
while (it.hasNext()) {
Entry entry = (Entry) it.next();
entry.printList(prefix + "/" + name);
}
}
}
使用例
public class Main {
public static void main(String[] args) {
try {
System.out.println("Making root entries...");
Directory rootdir = new Directory("root");
Directory bindir = new Directory("bin");
Directory tmpdir = new Directory("tmp");
Directory usrdir = new Directory("usr");
rootdir.add(bindir);
rootdir.add(tmpdir);
rootdir.add(usrdir);
bindir.add(new File("vi", 10000));
bindir.add(new File("latex", 20000));
rootdir.printList();
}catch (FileTreatmentException e) {
e.printStackTrace();
}
}
}
実行結果
Making root entries...
/root (30000)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (0)
まとめ
めちゃ端折ると
「容器」と「中身」に共通のスーパークラスを持って
「容器」にaddクラスを実装して容器と中身入れられるね再帰的でもあるねってだけかな
おわりに
こういう設計概念を学べるのがデザインパターン学習の醍醐味ですよね
現実的にはデザインパターンが言ってる実装したいならライブラリ使えばいいと思うですけど
設計概念は雰囲気で頭に取り入れとけば役に立つ日が来ると思います!