JavaでDecoratorパターン

# はじめに
[GoF](https://ja.wikipedia.org/wiki/ギャング・オブ・フォー_(情報工学))のデザインパターンを紹介している[『増補改訂版 Java言語で学ぶデザインパターン入門』](https://www.amazon.co.jp/増補改訂版Java言語で学ぶデザインパターン入門-結城-浩/dp/4797327030/ref=pd_lpo_sbs_14_t_0?_encoding=UTF8&psc=1&refRID=2ZE4GPYNN55JGDR5QMHP)を読んで、学んだ内容についてまとめます。

# Decoratorパターン
## Decoratorとは
日本語に訳すと「装飾者」を意味します。
**元となるオブジェクトにどんどんデコレート(装飾)を行うことで機能の拡張を行うようなパターン**のことを**Decoratorパターン**と言います。 
このパターンを適用することで、拡張元となるクラスに変更を加えることなく、柔軟に機能の拡張を行うことができます。

## 登場人物
Decoratorパターン使用するのは以下のクラス図に登場するクラスです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/278934/fcac2e57-dd72-8e44-09d4-6d44578b4eed.png)



### 抽象クラス
- **Component**   
装飾(拡張)される対象の元となる抽象クラスを表します。  

- **Decorator**  
`Component`クラスを継承する抽象クラスで、装飾を行う側のクラスの元となるクラスです。  特筆すべき点として、**装飾する対象である`Component`クラスをフィールドに持っている**点が挙げられます。

### 実装クラス
- **ConcreteComponent**
`Component`クラスの実装クラスです。Componentクラスの持つ抽象メソッドを全て実装します。

- **ConcreteDecorator**
`Decorator`クラスの実装クラスです。DecoratorクラスはComponentクラスを継承しているため、`Component`クラスの持つ抽象メソッドを実装する必要があります。

## 具体例
具体例として、以下のクラスをもとに説明します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/278934/caa8db32-9f7e-66e2-cc22-0cd96335409f.png)

### 抽象クラス
- **SpongeCakeクラス**  

```java:SpongeCake.java
public abstract class SpongeCake {
	public abstract String getName(); // 名前を得る

	public abstract int getPrice(); // 価格を得る

	public void show() { // 名前と価格を表示する
		System.out.println(getName() + ":" + getPrice() + "円");
	}
}
```
`SpongeCake`クラスでは抽象メソッドとして名前と価格の取得を行うメソッドを定義し、具象メソッドとしてそれらの表示を行うメソッドを定義しています。
ただのスポンジケーキで何の飾り付けもされていないイメージです。
抽象クラスの実際の処理については後述のサブクラスで実装します。
特段難しい点はありません。

- **Decoratorクラス**  

```java:Decorator.java
public abstract class Decorator extends SpongeCake {
	protected SpongeCake spongeCake; // この飾り枠がくるんでいる「中身」を指す

	protected Decorator(SpongeCake spongeCake) { // インスタンス生成時に「中身」を引数で指定
		this.spongeCake = spongeCake;
	}
}
```
`Decoratorクラス`は上述のSpongeCakeクラスを継承していますが、抽象メソッドのオーバーライドは行っていないため抽象クラスです。
**装飾の対象であるSpongeCakeオブジェクトをフィールドに持ち、コンストラクタでオブジェクトを生成する時の引数に指定している点**がポイントです。

### 実装クラス
- **ShortCakeクラス**

```java:ShortCake.java
public class ShortCake extends SpongeCake {

	@Override
	public String getName() { // 親クラスの名前を得るメソッドをオーバーライド
		return "ショートケーキ";
	}

	@Override
	public int getPrice() { // 親クラスの価格を得るメソッドをオーバーライド
		return 500;
	}

}
```
- **ChocolateCakeクラス**

```java:ChocolateCake.java
public class ChocolateCake extends SpongeCake {

	@Override
	public String getName() { // 親クラスの名前を得るメソッドをオーバーライド
		return "チョコレートケーキ";
	}

	@Override
	public int getPrice() { // 親クラスの価格を得るメソッドをオーバーライド
		return 700;
	}

}
```
`ShortCake`クラス、`ChocolateCake`クラスは`SpongeCake`クラスの実装クラスです。
スポンジだけのケーキに少し飾り付けを行い、より具体的なケーキにしたイメージです。
抽象メソッドである`getName()`メソッド、`getPrice()`メソッドをオーバーライドしており、それぞれ名前と価格を返しています。
ここでも特段難しい点はありません。

- **StrawberryDecoratorクラス**  

```java:StrawberryDecorator.java
public class StrawberryDecorator extends Decorator {

	public StrawberryDecorator(SpongeCake spongeCake) { // コンストラクタで飾り付けの対象を渡す
		super(spongeCake);
	}

	@Override
	public String getName() { // 中身の名前の先頭にイチゴを加えたもの
		return "イチゴ" + spongeCake.getName();
	}

	@Override
	public int getPrice() { // 中身の価格に100を加えたもの
		return spongeCake.getPrice() + 100;
	}

}
```
- **BananaDecoratorクラス**  

```java:BananaDecorator.java
public class BananaDecorator extends Decorator {

	public BananaDecorator(SpongeCake spongeCake) { // コンストラクタで飾り付けの対象を渡す
		super(spongeCake);
	}

	@Override
	public String getName() { // 中身の名前の先頭にバナナを加えたもの
		return "バナナ" + spongeCake.getName();
	}

	@Override
	public int getPrice() { // 中身の価格に300を加えたもの
		return spongeCake.getPrice() + 300;
	}

}
```
`StrawberryDecorator`クラス、`BananaDecorator`クラスはDecoratorクラスの実装クラスです。
それぞれ飾り付けとしてコンストラクタで指定されたスポンジケーキ(またはそのサブクラス)にイチゴとバナナを加えます。
また、価格についても同様に追加します。

ポイントは**コンストラクタの引数がSpongeCake型となっている点**です。
このことからコンストラクタに渡せる引数はSpongeCakeクラス、またはそのサブクラスであるShortCakeクラス、ChocolateCakeクラスであることが分かります。
また、忘れてはならないのがDecoratorクラスもSpongeCakeクラスのサブクラスであるということです。
つまり**装飾を行う`StrawberryDecorator`クラスのコンストラクタの引数には、Decoratorクラスを継承している`StrawberryDecorator`クラス、`BananaDecorator`クラスを指定可能である**ということです。
これにより装飾された対象に対して更に装飾を加えるということが可能になります。

### 実行クラス
- **Mainクラス**
  
```java:Main.java
public class Main {
	public static void main(String[] args) {
		// ショートケーキの生成
		SpongeCake s1 = new ShortCake();
		SpongeCake s2 = new StrawberryDecorator(s1);
		SpongeCake s3 = new BananaDecorator(s1);
		SpongeCake s4 = new StrawberryDecorator(new ShortCake());
		SpongeCake s5 = new StrawberryDecorator(new BananaDecorator(new ShortCake()));
		s1.show();
		s2.show();
		s3.show();
		s4.show();
		s5.show();
		System.out.println("--------------------------------------------");
		// チョコレートケーキの生成
		SpongeCake c1 = new ChocolateCake();
		SpongeCake c2 = new StrawberryDecorator(c1);
		SpongeCake c3 = new BananaDecorator(c1);
		SpongeCake c4 = new StrawberryDecorator(new ChocolateCake());
		SpongeCake c5 = new StrawberryDecorator(new BananaDecorator(new ChocolateCake()));
		c1.show();
		c2.show();
		c3.show();
		c4.show();
		c5.show();
	}
}
```
`ShortCake`クラスと`ChocolateCake`クラスのインスタンスを生成し、それぞれ装飾を行っています。
ポイントは`StrawberryDecorator`クラス、`BananaDecorator`クラスの説明でも述べたように、**コンストラクタの引数に装飾の対象として装飾を行うクラスが指定可能であるため、複数の装飾を行うことが可能であるという点**です。

また、

```java:4,5行目
SpongeCake s1 = new ShortCake();
SpongeCake s2 = new StrawberryDecorator(s1);
```
と

```java:7行目
SpongeCake s4 = new StrawberryDecorator(new ShortCake());
```
は装飾の対象の`ShortCake`をコンストラクタに指定しているだけで、書き方は違いますが実際には同じ処理です。

### 実行結果
`Main.java`を実行した結果は以下になります。
元となるShortCakeとChocolateCakeにどんどんと装飾がなされていることが確認できます。

```:実行結果
ショートケーキ:500円
イチゴショートケーキ:600円
バナナショートケーキ:800円
イチゴショートケーキ:600円
イチゴバナナショートケーキ:900円
--------------------------------------------
チョコレートケーキ:700円
イチゴチョコレートケーキ:800円
バナナチョコレートケーキ:1000円
イチゴチョコレートケーキ:800円
イチゴバナナチョコレートケーキ:1100円
```

## メリット
Decoratorパターンのメリットは以下になります。
**1.**継承元となるクラスに変更を加えずに機能の追加ができる
**2.**必要な部品を組み合わせることによって多様な機能を追加することができる

# まとめ
オブジェクトに次々と装飾を重ねていくDecoratorパターンに関して学びました。
以下でサンプルコードをアップしていますのでよろしければ参考にどうぞ。

- [Decoratorサンプルコード](https://github.com/masayuki777/study/tree/master/DesignPatterns/12_Decorator)

また、他のデザインパターンに関しては以下でまとめていますので、こちらも参考にどうぞ。

- [[随時更新]Javaでデザインパターンまとめ](https://qiita.com/mk777/items/4e13505b654fc21c76a2)

# 参考文献
- [増補改訂版 Java言語で学ぶデザインパターン入門](https://www.amazon.co.jp/増補改訂版Java言語で学ぶデザインパターン入門-結城-浩/dp/4797327030/ref=pd_lpo_sbs_14_t_0?_encoding=UTF8&psc=1&refRID=2ZE4GPYNN55JGDR5QMHP)