【C#】Head First デザインパターン3章　Decoratorパターンでグランデバニラアドリストレットショットアドチョコレートチップエクストラパウダーエクストラホイップ抹茶クリームフラペチーノ


##Decoratorパターン

https://www.oreilly.co.jp/books/4873112494/

今回は3章です。

デコレーターパターンはその名の通りすでにあるインスタンスにデコレートすることで機能を追加することができます。
機能を追加する際にすでにあるクラスを触る必要がないのに加え、コード内でいつでも動的に機能を追加することができるという利点があります。

本の中ではコーヒーの例があるので、スタバの商品を使って練習してみます。
[抹茶クリームフラペチーノ](https://product.starbucks.co.jp/beverage/frappuccino/4524785165908/?category=beverage%2Ffrappuccino)をカスタマイズしてグランデバニラアドリストレットショットアドチョコレートチップエクストラパウダーエクストラホイップ抹茶クリームフラペチーノにします。今回追加料金は値段のかからないもの以外は一括で55円にしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64828/66fb437f-cd93-aa62-8cc9-bce2e20b7144.png)

ベースとなる飲み物抽象クラスを使ってまずは抹茶フラペチーノを作ります。ショートは539円なので539を返します。

```Program.cs
  public abstract class Beverage
    {
        string description = "Unknown Beverage";

        public virtual string getDescription()
        {
            return description;
        }       

        public abstract double cost();
    }

 public class MatchaCreamFrap:Beverage
    {
        public override string getDescription()
        {
            return "Matcha Cream Frap";
        }

        public override double cost()
        {
            return 539;
        }
    }

```

###１．抽象クラス
今回グランデバニラアドリストレットショットアドチョコレートチップエクストラパウダーエクストラホイップ抹茶クリームフラペチーノを作るにあたって、エスプレッソショット、追加調味料、サイズ指定があるので、それらを事前に分類分けします。実際に使用する細かいもの（バニラシロップ等）は後に実装します。

```Program.cs
public abstract class CondimentDecorator : Beverage
    {
        protected Beverage beverage;
    }

    public abstract class SizeDecorator : Beverage
    {
        protected Beverage beverage;
    }

    public abstract class EspressoShots : Beverage
    {
        protected Beverage beverage;
    }
```


###２．調味料クラス
これをデコレートするのに調味料（チョコチップ等）Condimentsクラスを作ります。

```Program.cs
public class ChocolateChip : CondimentDecorator
    {
        Beverage beverage;

        public ChocolateChip(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string getDescription()
        {
            return beverage.getDescription() + ", Chocolate Chip";
        }

        public override double cost()
        {
            return 55 + beverage.cost();
        }
    }

    public class VanillaSyrup : CondimentDecorator
    {
        Beverage beverage;

        public VanillaSyrup(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string getDescription()
        {
            return beverage.getDescription() + ", Vanilla Syrup";
        }

        public override double cost()
        {
            return 55 + beverage.cost();
        }
    }

    public class MatchaPowder : CondimentDecorator
    {
        Beverage beverage;

        public MatchaPowder(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string getDescription()
        {
            return beverage.getDescription() + ", Matcha Powder";
        }

        public override double cost()
        {
            return 0 + beverage.cost();
        }
    }

    public class WhippedCream : CondimentDecorator
    {
        Beverage beverage;

        public WhippedCream(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string getDescription()
        {
            return beverage.getDescription() + ", Whipped Cream";
        }

        public override double cost()
        {
            return 0 + beverage.cost();
        }
    }
```

###３．サイズクラス
サイズもいくつかあるのでそちらも別のクラスとして書きます。飲み物によってサイズの値段が違う等あれば、各飲み物の中で条件分岐を書くのがよいかもしれません。スタバでは4サイズあり、それぞれ44円ずつ変わります。


```Program.cs
 public class Short : SizeDecorator
    {
        Beverage beverage;

        public Short(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string getDescription()
        {
            return beverage.getDescription() + ", Short";
        }

        public override double cost()
        {
            return 0 + beverage.cost();
        }
    }

    public class Tall : SizeDecorator
    {
        Beverage beverage;

        public Tall(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string getDescription()
        {
            return beverage.getDescription() + ", Tall";
        }

        public override double cost()
        {
            return 44 + beverage.cost();
        }
    }

    public class Grande : SizeDecorator
    {
        Beverage beverage;

        public Grande(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string getDescription()
        {
            return beverage.getDescription() + ", Grande";
        }

        public override double cost()
        {
            return 88 + beverage.cost();
        }
    }

    public class Venti : SizeDecorator
    {
        Beverage beverage;

        public Venti(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string getDescription()
        {
            return beverage.getDescription() + ", Venti";
        }

        public override double cost()
        {
            return 132 + beverage.cost();
        }
    }
```

###４．ショットクラス
ショットを実装します。普通のエスプレッソショットと量が少ないリストレットショットというものがあるのでそれらを追加します。

```Program.cs
public class Espresso : EspressoShots
    {
        Beverage beverage;

        public Espresso(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string getDescription()
        {
            return beverage.getDescription() + ", Espresso Shot";
        }

        public override double cost()
        {
            return 55 + beverage.cost();
        }
    }

    public class Ristretto: EspressoShots
    {
        Beverage beverage;

        public Ristretto(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string getDescription()
        {
            return beverage.getDescription() + ", Ristretto Shot";
        }

        public override double cost()
        {
            return 55 + beverage.cost();
        }
    }

```

###５．グランデバニラアドリストレットショットアドチョコレートチップエクストラパウダーエクストラホイップ抹茶クリームフラペチーノ
最後に抹茶フラペチーノをデコレートします。

```Program.cs
class Program
    {
        static void Main(string[] args)
        {                     
            Beverage matchaFrap = new MatchaCreamFrap();
            matchaFrap = new Grande(matchaFrap);
            matchaFrap = new ChocolateChip(matchaFrap);
            matchaFrap = new Ristretto(matchaFrap);
            matchaFrap = new VanillaSyrup(matchaFrap);
            matchaFrap = new MatchaPowder(matchaFrap);
            matchaFrap = new WhippedCream(matchaFrap);
            Console.WriteLine("{0}, Total: {1} Yen", matchaFrap.getDescription(), matchaFrap.cost());         
        }
    }

```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64828/d5644fb1-fef7-f629-3d9a-501d1113beb6.png)
グランデバニラアドリストレットショットアドチョコレートチップエクストラパウダーエクストラホイップ抹茶クリームフラペチーノは792円です。

シロップが足りない！！！！というときはたくさん気が済むまで足すことができます。
class Program
    {
        static void Main(string[] args)
        {
           
           
            Beverage matchaFrap = new MatchaCreamFrap();
            matchaFrap = new Grande(matchaFrap);
            matchaFrap = new ChocolateChip(matchaFrap);
            matchaFrap = new Ristretto(matchaFrap);
            matchaFrap = new VanillaSyrup(matchaFrap);
            matchaFrap = new VanillaSyrup(matchaFrap);
            matchaFrap = new VanillaSyrup(matchaFrap);
            matchaFrap = new VanillaSyrup(matchaFrap);
            matchaFrap = new VanillaSyrup(matchaFrap);
            matchaFrap = new MatchaPowder(matchaFrap);
            matchaFrap = new WhippedCream(matchaFrap);
            Console.WriteLine("{0}, Total: {1} Yen", matchaFrap.getDescription(), matchaFrap.cost());

            //Beverage dripCoffee = new DripCoffee();
            //dripCoffee = new VanillaSyrup(dripCoffee);
            //Console.WriteLine("{0}, Total: {1} Yen", dripCoffee.getDescription(), dripCoffee.cost());
        }
    }

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64828/898771c6-2a59-11b0-3879-ef60df52c57b.png)
この飲み物は恐らく食べ物よりもカロリーが多いでしょう。

Doxygen+GraphvizでBeverageクラスの図を出してみました。どのようになっているのか見ることができるので便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64828/22050356-85f5-7432-4f1d-3f910c5d2649.png)

バニラシロップ等は使いまわすことができるのでドリップコーヒークラスを足すことで、それにバニラシロップを足すこともできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64828/cef650ed-a5b6-74e4-538f-33cb11e44884.png)
バニラアドドリップコーヒーは374円でした。

```Program.cs
class Program
    {
        static void Main(string[] args)
        {                    
            Beverage dripCoffee = new DripCoffee();
            dripCoffee = new VanillaSyrup(dripCoffee);
            Console.WriteLine("{0}, Total: {1} Yen", dripCoffee.getDescription(), dripCoffee.cost());
        }
    }

 public class DripCoffee : Beverage
    {
        public override string getDescription()
        {
            return "Drip Coffee";
        }

        public override double cost()
        {
            return 319;
        }
    }

```