Solution to C ++ Design Pattern builder Pattern
C ++: builder Mode
Separates the construction of a complex object from its representation, so that different representations can be created during the same construction process.
I. Reasons
When constructing a window control, we usually have three initialization tasks:
UI initialization animation initialization signal slot Initialization
In this way, we can construct a window control. We can look at the following class diagram:
At first glance, there is no problem with this implementation.CenterWidget
Class is called in its constructorinitUi
,initAnimation
,initSlot
The three private member functions initialize the Ui, animation, and signal slot respectively. But when we start a newCenterWidget
During construction, the animation effect must be changed. What should I do at this time? ModifyinitAnimation
Member functions? As a good programmerModify classShould Be vigilant, this design obviously violatesPrinciple of opening/closing. At this time, we encountered a problem:The constructed interfaces are fixed, and the order of construction is fixed, which requires changes in the constructed content.
Intuitively, the solution to this problem is:initUi
,initAnimation
,initSlot
The three member functions must be virtual functions. When an animation, Ui, or signal slot needs to change, you only need to add a new subclass and rewrite a function. However, when these three member functions become virtual functions, they cannot be called in constructors because
During the construction of a class, the vtable has not been initialized, and the virtual function mechanism does not work correctly.
So we addedInit
Method, called after the object is constructed. The class diagram is as follows:
This class diagram solves the problem of the open and closed principle, but a new problem arises, and the client needs to callInit
YesInterface isolationAndSingle responsibilityWhy is this true?
The idea of the client is:
I want
CenterWidget
Then
CenterWidget
Displayed
WhileCenterWidget
Provided:
Here is
CenterWidget
The instance then calls
Init
Interface initialization and then
CenterWidget
Displayed
For the client, it depends on an interface that it does not need.Init
, ThisInit
Not as required by the client, but as needed by the clientCenterWidget
An interface forcibly matched as the logic function of the main window (Note: The interface isolation principle means that the client should not rely on interfaces that it does not need. The dependency of one class on the other class should be based on the smallest interface.). Second, as the main window classCenterWidget
Additional responsibilities are required to manage complex constructor logic. This is not a serious problem. This solution is often effective, but we propose a more elegant solution.
II. Implementation
To solve the problem described in the previous section, we willCenterWidget
Construct responsibility extraction from a separateBuilder
Class. In this wayinitUi
,initAnimation
,initSlot
Function TransferBuilder
Class. ByBuilder
ClassCenterWidget
Instance.initUi
,initAnimation
,initSlot
The order of the three function calls is required.Director
Class ManagementinitUi
,initAnimation
,initSlot
CommandBuilder
This isBuilder Mode. The builder mode and the builder ModeCenterWidget
Shows the construction scheme:
Builder Mode
Use the 'centerwidget 'solution in builder Mode
Iii. Code Analysis
BelowCenterWidget
Sample Code of the Solution
# Include
# Include
Using std: string; class CenterWidget {private: string Ui; string Animation; string Slot; public: virtual ~ CenterWidget () {}; void setUi (const string & x) {Ui = x;} void setAnimation (const string & x) {Animation = x ;} void setSlot (const string & x) {Slot = x;} void show () {std :: cout <"Ui =" <Ui <"Animation =" <Animation <"Slot =" <Slot <std: endl ;}}; class CenterWidgetBuilder {public: virtual ~ CenterWidgetBuilder () {} virtual void initUi () = 0; virtual void initAnimation () = 0; virtual void initSlot () = 0; virtual CenterWidget * getResult () = 0 ;}; class ConcreteCenterWidgetBuilderA: public CenterWidgetBuilder {private: CenterWidget * curWidget; public: ConcreteCenterWidgetBuilderA (): curWidget (new CenterWidget) {} virtual ~ ConcreteCenterWidgetBuilderA () {delete curWidget;}; virtual void initUi () {curWidget-> setUi ("Q Ui");} virtual void initAnimation () {curWidget-> setAnimation ("Biu ~ Biu ~ Biu ~ ");} Virtual void initSlot () {curWidget-> setSlot (" connected to your heart ");} CenterWidget * getResult () {return curWidget ;}; class ConcreteCenterWidgetBuilderB: public CenterWidgetBuilder {private: CenterWidget * curWidget; public: ConcreteCenterWidgetBuilderB (): curWidget (new CenterWidget) {} virtual ~ ConcreteCenterWidgetBuilderB () {delete curWidget;}; virtual void initUi () {curWidget-> setUi ("Q Ui");} virtual void initAnimation () {curWidget-> setAnimation ("Boom ~ Boom ~ Boom ~ ") ;}Virtual void initSlot () {curWidget-> setSlot (" connected to your heart ") ;}centerwidget * getResult () {return curWidget ;};}; class Dirctor {private: CenterWidgetBuilder * Builder; public: Dirctor (CenterWidgetBuilder * builder): Builder {}; virtual ~ Dirctor () {delete Builder;} void Construct () {Builder-> initUi (); Builder-> initAnimation (); Builder-> initSlot ();}}; int main (void) {ConcreteCenterWidgetBuilderA * builderA = new ConcreteCenterWidgetBuilderA; Dirctor * directorA = new Dirctor (builderA); directorA-> Construct (); builderA> getResult () -> show (); ConcreteCenterWidgetBuilderB * builderB = new ConcreteCenterWidgetBuilderB; Dirctor * directorB = n Ew Dirctor (builderB); directorB-> Construct (); builderB-> getResult ()-> show ();} running result: Ui = Q Ui Animation = Biu ~ Biu ~ Biu ~ Slot = connected to your heartUi = Q Ui Animation = Boom ~ Boom ~ Boom ~ Slot = connected to your heart
Duplicate and ConcreteCenterWidgetBuilderA have repeated code. To facilitate code reuse, we can use ConcreteCenterWidgetBuilderB to inherit ConcreteCenterWidgetBuilderA, and then rewrite the initAnimation to be changed. The code is modified as follows:
Class ConcreteCenterWidgetBuilderA: public CenterWidgetBuilder {protected: // change it to protected CenterWidget * curWidget for subclass access; public: ConcreteCenterWidgetBuilderA (): curWidget (new CenterWidget) {} virtual ~ ConcreteCenterWidgetBuilderA () {delete curWidget;}; virtual void initUi () {curWidget-> setUi ("Q Ui");} virtual void initAnimation () {curWidget-> setAnimation ("Biu ~ Biu ~ Biu ~ ");} Virtual void initSlot () {curWidget-> setSlot (" connected to your heart ");} CenterWidget * getResult () {return curWidget ;}; class ConcreteCenterWidgetBuilderB: public ConcreteCenterWidgetBuilderA {public: ConcreteCenterWidgetBuilderB () {} virtual ~ ConcreteCenterWidgetBuilderB () {}; // only rewrite the virtual void initAnimation () {curWidget-> setAnimation ("Boom ~ Boom ~ Boom ~ ") ;}}; Running result: Ui = Q Ui Animation = Biu ~ Biu ~ Biu ~ Slot = connected to your heartUi = Q Ui Animation = Boom ~ Boom ~ Boom ~ Slot = connected to your heart
Iv. Summary
Main advantages of the builder mode:
Decoupling the product itself from the product creation process allows different instances to be created during different creation processes to easily add new builders and create new product instances in line with the open and closed principles.
Main disadvantages of builder mode:
The builder mode can only create products with many commonalities. Similar components. If the product composition is complex and changeable, a large number of builder classes need to be defined to complicate the system.
Use Cases:
The product to be created has multiple components and the internal structure is complex. The process of creating an ordered object for the specified product must be independent of this class.