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