Decoratorパターン

# はじめに
GoFデザインパターンの一つで,構造に関するパターンである
**Decoratorパターン**について見ていきます.

# Decoratorパターンについて
DecoratorパターンはVisitorパターン同様に,
**既存の階層構造に新しい操作を追加**することができます.

また,新しい機能を追加しても既存のクラスに影響を与えることがありません.

そのため,オブジェクトに対してコードを変更させることなく追加の振る舞いを
実行時に与えたい場合,Decoratorパターンを適用します.

# Decoratorパターンの構造
[Decortorパターンとは|GoFデザインパターンの解説](https://cs-techblog.com/technical/decorator-pattern/)によると,構成要素は以下のようになります.
> - コンポーネント (Component)：基本機能を定義するインターフェースまたは抽象クラス
> - 具体的コンポーネント (Concrete Component)：インターフェースや抽象クラスを実装し、基本機能を提供するクラス
> - デコレーター (Decorator)：コンポーネントを保持し、機能を拡張する抽象クラスまたはインターフェース
> - 具体的デコレーター (Concrete Decorator)：デコレータークラスを拡張し、追加機能を実装するクラス

クラス図は以下のようになります.

#### クラス図
![スクリーンショット 2025-01-15 150433.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3586226/60ed2708-205d-8c8b-bdb0-461950344db3.png)


# Decoratorパターンの実装
ネット販売システムを例にして考えてみます.

以下は商品やサービスの購入を題材にしたクラス図とソースコードとなります.

#### クラス図
![スクリーンショット 2025-01-15 162445.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3586226/18be44bd-aab8-26b7-794a-ff332b3642cd.png)


#### ソースコード
```java:Purchase.java
public interface Purchase {
    void process();
}
```

```java:ProductPurchase.java
public class ProductPurchase implements Purchase {
    private String productName;

    public ProductPurchase(String productName) {
        this.productName = productName;
    }

    @Override
    public void process() {
        System.out.println(productName + " を購入しました。");
    }
}
```

```java:ServicePurchase.java
public class ServicePurchase implements Purchase {
    private String serviceName;

    public ServicePurchase(String serviceName) {
        this.serviceName = serviceName;
    }

    @Override
    public void process() {
        System.out.println(serviceName + " サービスを購入しました。");
    }
}
```

```java:PurchaseDecorator.java
public abstract class PurchaseDecorator implements Purchase {
    protected Purchase purchase;

    public PurchaseDecorator(Purchase purchase) {
        this.purchase = purchase;
    }

    @Override
    public void process() {
        purchase.process();
    }
}
```

```java:InsuranceDecorator.java
public class InsuranceDecorator extends PurchaseDecorator {
    public InsuranceDecorator(Purchase purchase) {
        super(purchase);
    }

    @Override
    public void process() {
        super.process();
        addInsurance();
    }

    private void addInsurance() {
        System.out.println("購入に保険を追加しました。");
    }
}
```

```java:DisountDecorator.java
public class DiscountDecorator extends PurchaseDecorator {
    public DiscountDecorator(Purchase purchase) {
        super(purchase);
    }

    @Override
    public void process() {
        super.process();
        applyDiscount();
    }

    private void applyDiscount() {
        System.out.println("割引を適用しました。");
    }
}
```

```java:DecoratorPatternExample.java
public class DecoratorPatternExample {
    public static void main(String[] args) {
        // 商品購入の基本処理
        Purchase productPurchase = new ProductPurchase("ノートパソコン");

        // サービス購入の基本処理
        Purchase servicePurchase = new ServicePurchase("オンラインコース");

        // 割引付きの商品購入
        Purchase discountedProduct = new DiscountDecorator(productPurchase);
        
        // 保険付きのサービス購入
        Purchase insuredDiscountedService = new DiscountDecorator(servicePurchase);

        // 処理を実行
        System.out.println("割引付き商品購入:");
        productPurchase.process();

        System.out.println("\n保険付きサービス購入:");
        insuredDiscountedService.process();
    }
}
```

```:出力例
商品購入:
ノートパソコン を購入しました。
割引を適用しました。

保険付きサービス購入:
オンラインコース サービスを購入しました。
購入に保険を追加しました。
```
各クラス概要は以下の通りです.

#### 1.Purchase(Component)
購入プロセスの基本となるインタフェースです.
processメソッドで購入処理を行います.

#### 2.ProductPurchase(ConcreteComponent)
商品の購入処理を実装するクラスです.

#### 3.ServicePurchase(ConcreteComponent)
サービスの購入処理を実装するクラスです.

#### 4.PurchaseDecorator(Decorator)
追加機能を拡張できるようにするためのDecoratorクラスです.

#### 5.InsuranceDecorator(ConcreteDecorator)
商品やサービスの購入に保険を追加するためのDecoratorクラスです.

#### 6.DiscountDecorator(ConcreteDecorator)
購入時に割引を適用するためのDecoratorクラスです.

# Decoratorパターンのメリット
Decoratorパターンを適用することによるメリットを見ていきます.

#### 機能の動的な追加
オブジェクトに対して**必要なときに機能を追加**することができます.

例えば,ある商品(ノートPC）を買うという処理に対して「割引適用」の機能を追加する際,
Decoratorを使うことで機能を追加することができます.

その他の機能も追加したければ,さらに別のDecoratorを用いることで
いくつもの機能に対して柔軟に対応することができます.
(必要がないなら取り外すこともできます)

また,**基本となっているクラス(ConcreteComponent)に対して変更を加えることなく
機能を追加**することができます.

# Decoratorパターンの問題点
Decoratorパターンを適用することによる問題点を見ていきます.

#### 構造の複雑化
Decoratorパターンを適用すると,多数のDecoratorクラスが作成され,
構造が複雑になり,それらの管理が難しくなる場合があります.

さらに,Decoratorは他のオブジェクトに依存するため,
**依存関係が複雑化**する可能性もあります.

そのため,他のDecoratorと重複する内容を含むDecoratorを作成する場合は,
新たに基本クラスとなるDecoratorを設計し,それを基に派生させることで,
重複を避けるなどの工夫をする必要があります.

# 参考
https://cs-techblog.com/technical/decorator-pattern/

https://refactoring.guru/ja/design-patterns/decorator