【コードあり】デコレーターパターンのc++版サンプルコード

勉強

今回はデザインパターンのオブザーバーパターンのサンプルコードを作成しました。

記事の作成に当たってインプットには以下の書籍やサイトを使って勉強しました。

間違い等あればコメントしてもらえると対応します。

デコレーターパターンの概要

デコレーターパターンは、デコレーターと呼ばれるラッパークラスを使用し、オブジェクトに新たに機能を追加することができるデザインパターンです。

元のクラスを変更することなく新たに機能を追加できる点がメリットです。

コンポーネント、具象コンポーネント、デコレーター、具象デコレータから構成され、それぞれ以下のような役割を持ちます。

  • コンポーネント : デコレーターとなるクラスやインターフェースを定義する。デコレーターと同じインターフェースを実装します。
  • 具象コンポーネント : コンポーネントの実装を提供するクラス。デコレーターがラップするオブジェクトの基本的な機能を実装します。
  • デコレーター : コンポーネントと同じインターフェースを持ち、そのコンポーネントをラップする。
  • 具象デコレーター : デコレーターの具体的な実装を提供するクラス。基本の機能に追加の機能を追加するために使用されます。

    構造はrefactoring.guruの図がわかりやすいのでそれ見て下さい。

    c++のサンプルコード

    今回はカフェでの注文をモデルにサンプルコードを作成してみました。

    コンポーネント(Beverage.h)

    デコレーターで使用する関数を定義します。

    #include <iostream>
    
    // コンポーネント(インターフェース)
    class Beverage {
    public:
        virtual std::string getDescription() const = 0;
        virtual double cost() const = 0;
        virtual ~Beverage() {}
    };

    デコレーター(CondimentDecorator.h)

    Beverageインターフェースを継承し、関数がコールされたときに代わりにメンバ変数で保持しているオブジェクト(具象コンポーネント)の関数をコールするようになっています。

    #include "Beverage.h"
    
    // デコレーター
    class CondimentDecorator : public Beverage {
    protected:
        Beverage* beverage;
    
    public:
        CondimentDecorator(Beverage* bev) : beverage(bev) {}
    
        std::string getDescription() const override {
            return beverage->getDescription();
        }
    
        double cost() const override {
            return beverage->cost();
        }
    };

    具象コンポーネント(Espresso.h)

    デコレーターによってコールされる関数を定義します。

    #include "Beverage.h"
    
    // 具象コンポーネント
    class Espresso : public Beverage {
    public:
        std::string getDescription() const override {
            return "Espresso";
        }
    
        double cost() const override {
            return 1.99;
        }
    };

    具象デコレーター(Milk.h, Syrup.h)

    具象デコレータは、具象コンポーネント(Espresso)をラップするために使用されます。そして、ラップされたオブジェクトを親クラスのコンストラクタに渡します。

    このようにすることで、具象コンポーネントのgetDescription関数がコールされたとき、EspressoのgetDscriptioonではなく、デコレーターのgetDescriptionがコールされるようになり、元々のEspressoのgetDescriptionは具象デコレータからコールされます。

    #include "CondimentDecorator.h"
    
    // 具象デコレーター - ミルク
    class Milk : public CondimentDecorator {
    public:
        Milk(Beverage* bev) : CondimentDecorator(bev) {}
    
        std::string getDescription() const override {
            return beverage->getDescription() + ", Milk";
        }
    
        double cost() const override {
            return beverage->cost() + 0.5; // ミルクは追加料金がかかる
        }
    };
    #include "CondimentDecorator.h"
    
    // 具象デコレーター - シロップ
    class Syrup : public CondimentDecorator {
    public:
        Syrup(Beverage* bev) : CondimentDecorator(bev) {}
    
        std::string getDescription() const override {
            return beverage->getDescription() + ", Syrup";
        }
    
        double cost() const override {
            return beverage->cost() + 0.25; // シロップは追加料金がかかる
        }
    };

    Main関数(main.cpp)

    #include "Espresso.h"
    #include "Milk.h"
    #include "Syrup.h"
    
    int main() {
        Beverage* beverage = new Espresso(); // ベースのコーヒーを注文
    
        // コーヒーのみの値段を確認
        std::cout << "Order: " << beverage->getDescription() << std::endl;
        std::cout << "Total cost: $" << beverage->cost() << std::endl;
        std::cout << "" << std::endl;
    
        // ミルクを追加
        beverage = new Milk(beverage);
    
        // シロップを追加
        beverage = new Syrup(beverage);
    
        std::cout << "Order: " << beverage->getDescription() << std::endl;
        std::cout << "Total cost: $" << beverage->cost() << std::endl;
    
        delete beverage; // メモリリークを防ぐために解放
    
        return 0;
    }

    サンプルコードの実行結果

    実行結果は以下のようになります。

    デコレーターを追加した結果が反映されています。

    Order: Espresso
    Total cost: $1.99
    
    Order: Espresso, Milk, Syrup
    Total cost: $2.74

    [topic color=”green” title=”関連記事”] [kanren url=”https://www.wata-blog.xyz/rare-subscription-matome/”] [kanren url=”https://www.wata-blog.xyz/manga_ikkiyomi_16/”] [kanren url=”https://www.wata-blog.xyz/daigakusei_blog_tuki5keta/”]

    その他おすすめの記事

    [/topic]

    コメント

    タイトルとURLをコピーしました