Decoratorパターン

### 概要
デザインパターンの一つ、Decorotorパターンの具体例。
Decorotorパターンは名前の通り、具体化クラスをDecoratorクラスで次々とデコレーションできる。

[書籍AdaptiveCode](https://honto.jp/ebook/pd-contents_0629112617.html)の単一責務の原則で紹介されていたが、フレームワークのみで特定用途を示すものがなかったので取り上げてみた。

### 参照コード
[wikipediaのDecorotorパターン](https://ja.wikipedia.org/wiki/Decorator_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)はJavaだったので、GPTでC#にしてもらい、投稿主がコメントを追加したもの。
~~~c#:example.cs
/** 価格をあらわすインタフェース */
interface IPrice
{
    int GetValue();
}

/** 原価を表すクラス */
class PrimePrice : IPrice
{
    private int value;

    public PrimePrice(int value)
    {
        this.value = value;
    }

    public int GetValue()
    {
        return this.value;
    }
}

/** マージンを介する価格 */
abstract class MarginPrice : IPrice
{
    protected IPrice originalPrice;

    public MarginPrice(IPrice price)
    {
        this.originalPrice = price;
    }

    public abstract int GetValue();
}

/** 設定された利益を仕入れ価格に上乗せする Price */
class WholesalePrice : MarginPrice
{
    private int advantage;

    public WholesalePrice(IPrice price, int advantage) : base(price)
    {
        this.advantage = advantage;
    }

    public override int GetValue()
    {
        return this.originalPrice.GetValue() + advantage;
    }
}

/** 仕入れ価格の 2 倍の値段を提示する Price */
class DoublePrice : MarginPrice
{
    public DoublePrice(IPrice price) : base(price)
    {
    }

    public override int GetValue()
    {
        return this.originalPrice.GetValue() * 2;
    }
}

class DecoratorTest
{
    static void Main(string[] args)
    {
        //PrimePriceをどんどんデコってる
        Console.WriteLine(
            new WholesalePrice(
                new DoublePrice(
                    new WholesalePrice(
                        new DoublePrice(
                            new PrimePrice(120)//valueが120のPrimePrice型のインスタンス
                        ), //GetValue()の返り値が240になるDoublePrice型のインスタンス
                        80
                    )//240+80=320
                ), //320*2=640
                200
            ).GetValue()
        );
    }
}
~~~
~~~:出力
840
~~~

### クラス図

![スクリーンショット 2023-04-09 10.47.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14345/5c97cc12-1560-8f5c-7f57-eec9e06d6ccf.png)


### ポイント
Decoratorクラス(DoublePrice、WholesalePrice)がIPrice型の参照をフィールドで持つ点がポイント。
それにより一回目のデコレーションでは具体化クラス(PrimePrice)のインスタンスを、二回目以降はひとつ前のDecoratorクラスのインスタンスを、それぞれコンストラクタで受け取れる。

### SOLID原則
参照コードが原則を守っているかいくつか取り上げる

#### 単一責務の原則
コード内の5つのクラスは依存関係が強く、1つのクラスにまとめられていてもおかしくない。
Decoratorパターンを使うことでクラスを最小化できている。

#### オープンクローズドの原則
機能追加にオープンで、機能追加しても既存のクラスは変更する必要がないためクローズド。
例えば消費税を価格に上乗せする機能が仕様追加された場合、下記の新クラスを追加するだけ。
~~~c#:example.cs
class TaxIncludedPrice : MarginPrice
{
    public TaxIncludedPrice(IPrice price) : base(price)
    {
    }

    /** 仕入れ価格の 1.1倍の値段にして消費税を加える */
    public override int GetValue()
    {
        var price = this.originalPrice.GetValue();
        return (int)(price * 1.1f);
    }
}
~~~
![スクリーンショット 2023-04-09 10.47.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14345/6f508633-835b-88db-e93d-d0cd8e108207.png)

#### リスコフの置換原則
継承先のDoublePriceやWholesalePriceで新しくメソッド定義されてないので守られている。

## 関連URL
[C#で学ぶデザインパターン入門 ⑫Decorator Pattern](https://qiita.com/toshi0607/items/a65d1abb95a97970ab8c)