I recently tried to implement the decorator design pattern in C++. Here is the code in full:
#include <algorithm>
#include <iostream>
#include <memory>
#include <numeric>
#include <string>
#include <vector>
//Interface for items of food
class Food {
public:
virtual float Price() const = 0; //The price of the food in some currency
virtual std::vector<std::string> IngredientsList() const = 0;
virtual ~Food() = default;
};
//An example of an item of food (concrete component)
class Pizza : public Food {
public:
float Price() const override {return _base_cost;};
std::vector<std::string> IngredientsList() const override {return {"Dough"};}
private:
float _base_cost{3.0}; //The cost of a pizza with no toppings
};
//Interface for decorators
class IngredientsDecorator : public Food {
public:
explicit IngredientsDecorator(Food* food) : _food{food} {}
virtual float Price() const override {return _food->Price();}
virtual std::vector<std::string> IngredientsList() const override {return _food->IngredientsList();}
protected:
Food* _food;
};
//An example of a decorator
class MozzarellaDecorator : public IngredientsDecorator {
public:
explicit MozzarellaDecorator(std::shared_ptr<Food> food)
: IngredientsDecorator{food.get()} {}
float Price() const override {return IngredientsDecorator::Price() + _cost;};
std::vector<std::string> IngredientsList() const override {
auto AllIngredients = IngredientsDecorator::IngredientsList();
auto MozzarellaIngredients = Ingredients();
AllIngredients.insert(AllIngredients.end(), MozzarellaIngredients.begin(), MozzarellaIngredients.end());
return AllIngredients;
}
private:
float _cost{0.50};
std::vector<std::string> Ingredients() const {return {"Mozzarella"};};
};
//An order consists of a number of food items
class Order {
public:
void AddToOrder(std::shared_ptr<Food> food) {
_food.push_back(food.get());
}
void ClearOrder() {_food.clear();}
float Price() const {
return std::accumulate(_food.begin(),_food.end(),0.f, [](float a, Food* b) -> float {return a + b->Price();});
}
std::vector<std::string> IngredientsList() const {
std::vector<std::string> all_ingredients;
std::for_each(_food.begin(),
_food.end(),
[&all_ingredients](Food* f) {
auto curr_ingredients = f->IngredientsList();
all_ingredients.insert(all_ingredients.end(), curr_ingredients.begin(), curr_ingredients.end());
});
return all_ingredients;
}
void PrintOrderInformation() const {
std::cout<<Price()<<'\n';
auto ingredients{IngredientsList()};
for(const auto& e : ingredients) {
std::cout<<e<<'\n';
}
std::cout<<"\n-------------\n";
};
private:
std::vector<Food*> _food;
};
int main()
{
//Create an empty order
Order my_order;
//Add a mozzarella pizza
auto pizza_base = std::make_shared<Pizza>();
auto mozzarella_pizza = std::make_shared<MozzarellaDecorator>(pizza_base);
my_order.AddToOrder(mozzarella_pizza);
my_order.PrintOrderInformation();
my_order.ClearOrder();
//New order for a pizza with double mozzarella
auto double_mozzarella_pizza = std::make_shared<MozzarellaDecorator>(mozzarella_pizza);
my_order.AddToOrder(double_mozzarella_pizza);
my_order.PrintOrderInformation();
my_order.ClearOrder();
}
My questions are the following:
- Have I implemented the decorator pattern correctly?
- Have I used smart pointers correctly? I'm not 100% sure whether the functions should take a smart pointer or a raw pointer.
- Is there a way to compose decorators in a constructor? Specifically, is there a way I can write the following
auto base_pizza = std::make_shared<Pizza>();
auto single_mozzarella = std::make_shared<MozzarellaDecorator>(base_pizza);
as something like this instead?
auto single_mozzarella = std::make_shared<MozzarellaDecorator>(std::make_shared<Pizza>());