Decorator Pattern C + + implementation of design pattern

Posted by TripleDES on Tue, 28 Sep 2021 20:12:39 +0200

Refer to the book Head First design pattern


Design pattern and design principle

        Decorator Pattern dynamically attaches responsibility to objects. To extend functionality, decorator pattern provides a more flexible alternative than inheritance.

The decorator pattern follows the following design principles:

       Classes should be open to extensions and closed to modifications.

Classes in Decorator Pattern

        The classes in decorator mode are shown in the figure below, with picture links 23 design modes - decorator mode_ Uncle Yang - CSDN blog , invasion and deletion.

         The Component abstract Component class is the decorated class. Each Component can be used alone or wrapped by the decorator. Some interfaces are declared in this class, which will be implemented in specific components and specific decorators.         

         ConcreteComponent concrete component class, which inherits from the component class, is an object that we want to dynamically add new behavior (i.e. decoration).

         Decorator Abstract decorator class is the base class of concrete decorator. Decorator class contains a pointer of component type to record the decorated object. When you need to obtain the behavior after decoration, you can obtain the behavior of the decorated person plus the behavior of the decorator itself through this pointer, which will be seen in the following case.

         ConcreteDecorator is a specific Decorator class. The Decorator class needs to implement the methods defined in Decorator. In addition, some new methods can be added.

          As shown in the above figure, the decorator and the decorated must be of the same type, that is, they have a common superclass, which is a very key place. This is because the decorator must be able to replace the decorated.

Case description

       The coffee shop offers a variety of coffees, each with different spices (Mocha, milk foam, disaccharide, semi sugar, etc.). Drinks are the abstract components, various coffees are the concrete components, and different condiments in coffee are the decorators. Each coffee and condiment has its own description and price. Using the decorator mode, the description and price of coffee with different condiments can also be easily given.

        In the following example, three kinds of coffee are produced: Espresso without seasoning, dark toast with double mocha and one mocha and HouseBlend with double mocha.


code implementation

          Declaration: the declaration and implementation of a class in the same file is a bad habit, a bad habit, a bad habit, but because I'm lazy, I still write it together. Don't follow suit. Take a warning, take a warning, take a warning.

        First, define the abstract component class coverage and the abstract decorator class CondimentDecorator. The code is as follows. The key here is that the abstract decorator inherits from the abstract component and contains a reference to the abstract component.

//Abstract component class - Beverage
class Beverage
{
public:
	Beverage() :m_description("Unknown Beverage")
	{

	}
	virtual std::string getDescription(){ return m_description; }
	virtual double cost() = 0;
protected:
	std::string m_description;
};

//Abstract decorator class - seasoning, inherited from beverage class
class CondimentDecorator :public Beverage
{
public:
	CondimentDecorator(Beverage* berverge) 
		:m_beverage(berverge)
	{

	}
	virtual std::string getDescription() = 0;//It is defined as a pure virtual function to force subclass instantiation to implement it.
protected:
	Beverage* m_beverage;
};

        Then define specific components, that is, three specific coffee types: darkroad, Espresso and HouseBlend.

//Three specific components
class DarkRoast :public Beverage
{
public:
	DarkRoast()
	{
		m_description = "DarkRoast";
	}
	double cost()
	{
		return 2.99;
	}
};

class Espresso :public Beverage
{
public:
	Espresso()
	{
		m_description = "Espresso";
	}
	double cost()
	{
		return 1.99;
	}
};

class HouseBlend :public Beverage
{
public:
	HouseBlend()
	{
		m_description = "HouseBlend";
	}
	double cost()
	{
		return 0.89;
	}
};

        Then define two specific decorator classes, namely Mocha and Milk.

//Two specific decorators
class Mocha :public CondimentDecorator
{
public:
	Mocha(Beverage* beverage) :CondimentDecorator(beverage)
	{
	}

	std::string getDescription()
	{
		return  m_beverage->getDescription() + " Mocha";
	}

	double cost()
	{
		return 0.2 + m_beverage->cost();
	}
};


class Milk :public CondimentDecorator
{
public:
	Milk(Beverage* beverage) :CondimentDecorator(beverage)
	{
		
	}


	std::string getDescription()
	{
		return  m_beverage->getDescription() + " Milk";
	}

	double cost()
	{
		return 0.5 + m_beverage->cost();
	}
};

         Finally, write the test code in the main function to produce three kinds of coffee: Espresso without seasoning, darkroad with double mocha and HouseBlend with double mocha

//Test code
int main()
{
	//Espresso without seasoning
	Beverage* beverage = new Espresso();
	std::cout << beverage->getDescription() << " ¥" << beverage->cost() << std::endl;

	//Dark toast with double mocha and milk foam
	Beverage* beverage2 = new DarkRoast();
	beverage2 = new Mocha(beverage2);
	beverage2 = new Mocha(beverage2);
	beverage2 = new Milk(beverage2);
	std::cout << beverage2->getDescription() << " ¥" << beverage2->cost() << std::endl;

	//Double foamed and a Mocha HouseBlend
	Beverage* beverage3 = new HouseBlend();
	beverage3 = new Mocha(beverage3);
	beverage3 = new Milk(beverage3);
	beverage3 = new Milk(beverage3);
	std::cout << beverage3->getDescription() << " ¥" << beverage3->cost() << std::endl;

	system("pause");
	delete beverage;
	delete beverage2;
	delete beverage3;
}

   

Operation results

 

Topics: C++ Design Pattern