Design Pattern | Decorator Pattern(デコレーターパターン)
Decoratorパターンとは?
- Decoratorという英単語は装飾する(Decorate)という意味である。既存の内容に何かを付け加えるという意味合いが強い。
- Decoratorパターンは、オブジェクトに続けてデコレーション(装飾)を追加する方式である。
- ピザを例にすると、既存のピザの上にトッピングを追加すると考えればよい。
- スポンジケーキにクリーム、チョコレート、いちごなどで装飾できるように、オブジェクトにも機能を1つずつかぶせて装飾する方式である。
- GoFデザインパターンでは、構造に関するデザインパターンに分類される。
Decoratorパターンのサンプルプログラム
入力した文字列に対して、枠線などの装飾を行うサンプルプログラムである。
Class Diagram

1. Displayクラス
文字列行表示用の抽象クラスである。
Display.java
package com.devkuma.designpattern.structural.decorator;
public abstract class Display {
// 列の文字数を返す。
public abstract int getColumns();
// 行数を返す。
public abstract int getRows();
// 指定された行の文字列を返す。
public abstract String getRowText(int row);
public void show() {
for (int i = 0; i < getRows(); i++) {
System.out.println(getRowText(i));
}
}
}
2. StringDisplayクラス
1行だけで構成される文字列行表示用のクラスである。
StringDisplay.java
package com.devkuma.designpattern.structural.decorator;
public class StringDisplay extends Display {
private String string;
public StringDisplay(String string) {
this.string = string;
}
public int getColumns() {
return string.getBytes().length;
}
public int getRows() {
return 1;
}
public String getRowText(int row) {
return (row == 0) ? string : null;
}
}
3. Borderクラス
装飾枠を表す抽象クラスである。
Border.java
package com.devkuma.designpattern.structural.decorator;
public abstract class Border extends Display {
protected Display display;
protected Border(Display display) {
this.display = display;
}
}
4. SideBorderクラス
左右に装飾枠を付けるクラスである。
SideBorder.java
package com.devkuma.designpattern.structural.decorator;
public class SideBorder extends Border {
public SideBorder(Display display) {
super(display);
}
public int getColumns() {
// 文字数は内容の両側に装飾した文字数を加えた数。
return 1 + display.getColumns() + 1;
}
public int getRows() {
// 行数は内容の行数と同じ。
return display.getRows();
}
public String getRowText(int row) {
return "*" + display.getRowText(row) + "*";
}
}
5. FullBorderクラス
上下左右に装飾枠を付けるクラスである。
FullBorder.java
package com.devkuma.designpattern.structural.decorator;
public class FullBorder extends Border {
public FullBorder(Display display) {
super(display);
}
public int getColumns() {
// 文字数は内容の両側に左右の装飾文字数を加えた数。
return 1 + display.getColumns() + 1;
}
public int getRows() {
// 行数は内容の行数に上下の装飾文字数を加えた数。
return 1 + display.getRows() + 1;
}
public String getRowText(int row) {
if (row == 0) {
// 上端の枠
return "+" + makeLine('-', display.getColumns()) + "+";
} else if (row == display.getRows() + 1) {
// 下端の枠
return "+" + makeLine('-', display.getColumns()) + "+";
} else {
// その他
return "|" + display.getRowText(row - 1) + "|";
}
}
private String makeLine(char ch, int count) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < count; i++) {
buf.append(ch);
}
return buf.toString();
}
}
6. Mainクラス
メイン処理を実行するクラスである。
Main.java
package com.devkuma.designpattern.structural.decorator;
public class Main {
public static void main(String[] args) {
Display display1 = new StringDisplay("Hello world");
display1.show();
System.out.println("");
Display display2 = new SideBorder(display1);
display2.show();
System.out.println("");
Display display3 = new FullBorder(display2);
display3.show();
System.out.println("");
Display display4 =
new FullBorder(
new SideBorder(
new FullBorder(
new StringDisplay("Hello world"))));
display4.show();
}
}
7. 実行結果
Hello world
*Hello world*
+-------------+
|*Hello world*|
+-------------+
+---------------+
|*+-----------+*|
|*|Hello world|*|
|*+-----------+*|
+---------------+
Decoratorパターンのメリット
Decoratorパターンでは、装飾枠(Border)も中身(StringDisplay)も共通のインターフェースを持っている。インターフェースは共通だが、包めば包むほど機能が追加されていく。そのとき、包まれる側を修正する必要はない。包むものを変更せずに機能を追加できる。