Skip to content

Commit 33b47f6

Browse files
committed
[PROTOTYPE]: Added infos and example code for Prototype pattern.
1 parent afd76ac commit 33b47f6

File tree

3 files changed

+168
-2
lines changed

3 files changed

+168
-2
lines changed
Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,55 @@
1-
# TODO
1+
# Intend
22

3-
Stay tuned, this section will be completed shortly !
3+
Specify the kind of objects to create using a prototypical instance, and **create new objects by copying this prototype**.
4+
5+
It allows you to **clone** (even complex) **objects without coupling
6+
to their specific classes**.
7+
8+
You should use the _Prototype_ pattern in the following cases :
9+
- Whenever creation of new object requires setting many parameters and some (or all) are optional.
10+
- To avoid building a class hierarchy of factories.
11+
- When instances of a class can only have a few different states.
12+
- When the class to instantiate are specified at run-time (_dynamic loading_)
13+
14+
# How it's done
15+
16+
![UML](UML.jpg)
17+
18+
**Participants**
19+
20+
- _Prototype_ : Defines an interface to clone itself.
21+
- _ConcretePrototype_ : Implement the interface of _Prototype_.
22+
- _Client_ : **Creates new objects** by asking a _Prototype_ to clone itself.
23+
24+
**How to implement**
25+
26+
1. __Create the *Prototype* interface__ (with the clone() method).
27+
2. __Create the *ConcretePrototypes*__ that implement the _Prototype_ interface to copy() themselves.
28+
3. Optionally, __Add a registry__
29+
- It is easy to define a **Registry** using the [**Factory**](../factory-method/README.md) template that will **store a catalog of frequently used prototype**.
30+
- As the client ask for a Prototype (based on any search criteria such as a enum or a combination of params...), the Registry will find it in its catalog and retrieve a copy() of it.
31+
- This will simplify the management of the different possible prototypes as well as provide a _unique point of creation_.
32+
33+
Note : UML class diagram taken from [**here**](https://upload.wikimedia.org/wikipedia/commons/c/c4/W3sDesign_Prototype_Design_Pattern_UML.jpg)
34+
35+
# Pros & cons
36+
37+
**Pros**
38+
- __Readability__ : Produce complex objects more conveniently.
39+
You will also get rid of repeated initialization code in favor of cloning pre-built prototypes.
40+
- __Reducing subclassing__
41+
- __Dynamically configure__ an application
42+
43+
**Cons**
44+
- Implementing the _clone()_ operation can be difficult when object structures contain _circular references_.
45+
- Shallow copy vs deep copy problem : Should the _clone()_ operation make a copy of the object parameters or should the original and clone share the parameters ?
46+
- Initializing clones : The client might want to initialize some of the internal states of its clone. But you cannot pass those as parameters to your _clone()_ operation (because their numbers will vary between prototypes).
47+
The client might have to use setters (if you provided them) just after the _clone()_ operation or you might need to provide him with a _initialize()_ operation.
48+
49+
# Notes
50+
51+
The _Prototype pattern_ is **available in C++** with a **Cloneable interface**.
52+
53+
Here are some _usefull ressources_ :
54+
- A [Refactoring guru](https://refactoring.guru/design-patterns/prototype/cpp/example) article.
55+
- A complete example [here](http://www.vishalchovatiya.com/builder-design-pattern-in-modern-cpp/)

creational-patterns/prototype/UML.jpg

43.1 KB
Loading
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#include <iostream>
2+
#include <vector>
3+
#include <memory>
4+
#include <unordered_map>
5+
6+
typedef enum {
7+
XML = 1,
8+
PLAIN = 2,
9+
SPREAD = 3
10+
} DocType;
11+
12+
/*
13+
* @brief Prototype declares an interface to clone itself.
14+
*/
15+
class DocPrototype
16+
{
17+
public:
18+
virtual std::unique_ptr<DocPrototype> clone(void) const = 0;
19+
virtual void print(void) const = 0;
20+
virtual ~DocPrototype() { }
21+
};
22+
23+
/*
24+
* @brief : ConcretePrototypes : xmlDoc, plainDoc, spreadsheetDoc
25+
* Implement the interface of the Prototype to be able
26+
* to clone themselves.
27+
*/
28+
class xmlDoc : public DocPrototype
29+
{
30+
public:
31+
xmlDoc(void) {}
32+
~xmlDoc() {}
33+
34+
std::unique_ptr<DocPrototype> clone(void) const override {
35+
return std::make_unique<xmlDoc>(*this);
36+
}
37+
void print(void) const override { std::cout << "xmlDoc\n"; }
38+
};
39+
40+
class plainDoc : public DocPrototype
41+
{
42+
public:
43+
plainDoc(void) {}
44+
~plainDoc() {}
45+
46+
std::unique_ptr<DocPrototype> clone(void) const override {
47+
return std::make_unique<plainDoc>(*this);
48+
}
49+
void print(void) const override { std::cout << "plainDoc\n"; }
50+
};
51+
52+
class spreadsheetDoc : public DocPrototype
53+
{
54+
public:
55+
spreadsheetDoc(void) {}
56+
~spreadsheetDoc() {}
57+
58+
std::unique_ptr<DocPrototype> clone(void) const override {
59+
return std::make_unique<spreadsheetDoc>(*this);
60+
}
61+
void print(void) const override { std::cout << "spreadsheetDoc\n"; }
62+
};
63+
64+
/*!
65+
* @brief : FactoryManager contains one instance of each
66+
* ConcretePrototype so that it can use their
67+
* clone() method to provide the client with the
68+
* required ConcretePrototype.
69+
*/
70+
class DocFactoryManager {
71+
public:
72+
DocFactoryManager(void) {
73+
m_docs[XML ] = std::make_unique<xmlDoc >();
74+
m_docs[PLAIN ] = std::make_unique<plainDoc >();
75+
m_docs[SPREAD] = std::make_unique<spreadsheetDoc>();
76+
}
77+
~DocFactoryManager(){}
78+
79+
std::unique_ptr<DocPrototype> makeDocument( DocType type ) {
80+
return m_docs[type]->clone();
81+
}
82+
83+
private:
84+
std::unordered_map<DocType,
85+
std::unique_ptr<DocPrototype> > m_docs;
86+
};
87+
88+
/*!
89+
* @brief : Client code
90+
* Only uses the FactoryManager to create the document
91+
* type he needs.
92+
*/
93+
void clientCode(DocFactoryManager& mngr) {
94+
std::vector<std::unique_ptr<DocPrototype> > clientDocs;
95+
int clientChoice;
96+
97+
std::cout << "quit(0), xml(1), plain(2), spreadsheet(3): \n";
98+
while (true)
99+
{
100+
std::cout << "Type in your choice (0-3)\n";
101+
std::cin >> clientChoice;
102+
if ( clientChoice <= 0 || clientChoice > SPREAD ) break;
103+
clientDocs.push_back( mngr.makeDocument( static_cast<DocType>(clientChoice) ) );
104+
}
105+
106+
for ( auto& it : clientDocs ) it->print();
107+
}
108+
109+
int main() {
110+
DocFactoryManager mngr;
111+
clientCode(mngr);
112+
113+
return 0;
114+
}

0 commit comments

Comments
 (0)