概要
「Rubyによるデザインパターン」を読んでデザインパターンを勉強中。
Javaをやっていた人間としての目線で情報を整理してみます。
####他のデザインパターン
Template Method Pattern
Strategy Pattern
Observer Pattern
Composite Pattern
- 全体と部分を同一視する、というパターン
- 入れ物(Composite)と中身(Leaf または Composite)が同じインターフェースを持つことでツリーのような再帰的な構造を簡潔に表現できる
実装例
HTML文書を表すクラスを考えてみました。
<html>
<head>
<title>hoge</title>
</head>
</html>
HTMLは <html>
、<head>
などのタグの中にさらに別のタグ、もしくはテキスト(この場合 <title>
タグの中の "hoge")が含まれる、というような再帰的な構造になっているので、タグを Composite 、テキストを Leaf クラスとしています。
Java での実装
Component
HtmlElement.java
public abstract class HtmlElement {
final String INDENT = " ";
// depth 分の空白文字を生成します
protected String indent(int depth) {
StringBuilder sb = new StringBuilder();
for (int i=0; i<depth; i++) {
sb.append(INDENT);
}
return sb.toString();
}
public abstract void print(int depth);
}
print
を抽象メソッドとして定義し、共通のインターフェースとしています。
Composite
Tag.java
import java.util.List;
import java.util.ArrayList;
import java.io.PrintStream;
public class Tag extends HtmlElement {
private String name;
private List<HtmlElement> children; // 子要素は HtmlElement 型
public Tag(String name) {
this.name = name;
this.children = new ArrayList<HtmlElement>();
}
public Tag addChild(HtmlElement child) {
children.add(child);
return this;
}
// 開始タグ、終了タグで囲んでその間に子要素を出力します
// このとき子要素が Tag なのか Text なのか を意識しなくて済む、というところがポイント
@Override
public void print(int depth) {
System.out.printf("%s<%s>%n", indent(depth), name);
for (HtmlElement child : children) {
child.print(depth + 1);
}
System.out.printf("%s</%s>%n", indent(depth), name);
}
}
Leaf
Text.java
import java.io.PrintStream;
public class Text extends HtmlElement {
private String text;
public Text(String text) {
this.text = text;
}
@Override
public void print(int depth) {
System.out.println(indent(depth) + text);
}
}
呼び出し
Main.java
public class Main {
public static void main(String[] args) {
Tag root = new Tag("html");
Tag head = new Tag("head");
Tag title = new Tag("title").addChild(new Text("Sample code for Composite Pattern"));
head.addChild(title);
root.addChild(head);
Tag body = new Tag("body");
Tag h1 = new Tag("h1").addChild(new Text("What is Composite Pattern?"));
Tag p = new Tag("p")
.addChild(new Text("In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes that a group of objects is to be treated in the same way as a single instance of an object. "));
body.addChild(h1);
body.addChild(p);
root.addChild(body);
root.print(0);
}
}
実行結果
<html>
<head>
<title>
Sample code for Composite Pattern
</title>
</head>
<body>
<h1>
What is Composite Pattern?
</h1>
<p>
In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes that a group of objects is to be treated in the same way as a single instance of an object.
</p>
</body>
</html>
Ruby での実装
Component
class HtmlElement
INDENT = " ";
def indent(depth)
INDENT * depth
end
end
Java で抽象メソッドとしていたメソッドを Component に置くべきか?は悩みどころ。デフォルトの挙動が無ければ不要かもしれません。
Composite
class Tag < HtmlElement
def initialize(name)
@name = name
@children = []
end
def add_child(child)
@children << child
self
end
def print(depth=0)
puts indent(depth) + "<#{@name}>"
@children.each do |child|
child.print(depth + 1)
end
puts indent(depth) + "</#{@name}>"
end
end
Leaf
class Text < HtmlElement
def initialize(text)
@text = text
end
def print(depth=0)
puts indent(depth) + @text
end
end
呼び出し
root = Tag.new("html")
head = Tag.new("head")
title = Tag.new("title").add_child(Text.new("Sample code for Composite Pattern"))
head.add_child(title)
root.add_child(head)
body = Tag.new("body")
h1 = Tag.new("h1").add_child(Text.new("What is Composite Pattern?"))
p = Tag.new("p").add_child(Text.new("In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes that a group of objects is to be treated in the same way as a single instance of an object. "))
body.add_child(h1)
body.add_child(p)
root.add_child(body)
root.print
実行結果
<html>
<head>
<title>
Sample code for Composite Pattern
</title>
</head>
<body>
<h1>
What is Composite Pattern?
</h1>
<p>
In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes that a group of objects is to be treated in the same way as a single instance of an object.
</p>
</body>
</html>
参考
Olsen, R. 2007. Design Patterns in Ruby