DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Prototype Pattern in JavaScript
  • Design Pattern: What You Need to Know to Improve Your Code Effectively
  • Accessibility Testing for Digital Products
  • Object Relational Behavioral Design Patterns in Java

Trending

  • Subtitles: The Good, the Bad, and the Resource-Heavy
  • How to Format Articles for DZone
  • Develop a Reverse Proxy With Caching in Go
  • Scalable System Design: Core Concepts for Building Reliable Software
  1. DZone
  2. Coding
  3. Languages
  4. Prototype Design Pattern in Modern C++

Prototype Design Pattern in Modern C++

By 
Vishal Chovatiya user avatar
Vishal Chovatiya
·
Jul. 21, 20 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
10.7K Views

Join the DZone community and get the full member experience.

Join For Free

Prototype Design Pattern is a Creational Design Pattern that helps in the prototyping(creating/copying cheaply) of an object using separate methods or polymorphic classes. You can consider the prototype as a template of an object before the actual object is constructed. In this article, we're going to take a look at why we need a Prototype Design Pattern in C++ i.e. motivation, prototype factory and leveraging prototype design pattern to implement virtual copy constructor. 

By the way, If you haven't check out my other articles on Creational Design Patterns, then here is the list:

  1.  Factory
  2.  Builder
  3.  Prototype
  4.  Singleton

The code snippets you see throughout this series of articles are simplified. So, you'll often see me not using keywords like override, final, public just to make code compact and consumable (most of the time) in a single standard screen size. I also prefer struct instead of class just to save a line by not writing "public:" sometimes and also miss virtual destructor, constructor, copy constructor, prefix std::, deleting dynamic memory, intentionally. I also consider myself a pragmatic person who wants to convey an idea in the simplest way possible rather than the standard way or using jargon.

Note:

  • If you stumbled here directly, then I would suggest you go through What is design pattern? first, even if it is trivial. I believe it will encourage you to explore more on this topic.
  • All of this code you encounter in this series of articles are compiled using C++20(though I have used Modern C++ features up to C++17 in most cases). So if you don't have access to the latest compiler you can use https://wandbox.org/, which has preinstalled boost library as well.

Intent

To create a new object cheaply with the help of an already constructed or pre-initialized stored object.

cells multiplying

  • The prototype provides flexibility to create complex objects cheaply. The concept is to copy an existing object rather than creating a new instance from scratch, something that may include costly operations.
  • The existing object then acts as a prototype, and the newly copied object may change the same properties only if required. This approach saves resources and time, especially when object creation is a heavy process.
  • So, essentially, the prototype is quite simply a partially or fully initialized object that you make a copy of. And then you subsequently use for your own benefit with variations.

Motivation

C++
xxxxxxxxxx
1
21
 
1
struct Office {
2
    string         m_street;
3
    string         m_city;
4
    int32_t         m_cubical;
5
6
    Office(string s, string c, int32_t n):m_street(s), m_city(c), m_cubical(n){}
7
};
8
9
struct Employee {
10
    string      m_name;
11
    Office        m_office;
12
13
    Employee(string n,  Office o):m_name(n), m_office(o){}
14
};
15
16
int main() {
17
    Employee john{ "John Doe", Office{"123 East Dr", "London", 123} };
18
    Employee jane{ "Jane Doe", Office{"123 East Dr", "London", 124} };
19
    Employee jack{ "jack Doe", Office{"123 ORR", "Bangaluru", 300} };
20
    return EXIT_SUCCESS;
21
}


This is not the right approach, as you have to write the main office address again and again for each employee detail. This is cumbersome and becomes more difficult to work with when you want to create an employee list. Moreover, consider the situation when your main office moves to another address.

Prototype Design Pattern Examples in C++

A more pragmatic approach would be like this :

C++
xxxxxxxxxx
1
16
 
1
struct Employee {
2
    string          m_name;
3
    const Office*   m_office;    
4
    
5
    Employee(string n,  Office *o):m_name(n), m_office(o){}
6
};
7
8
static Office   LondonOffice{"123 East Dr", "London", 123};
9
static Office   BangaluruOffice{"RMZ Ecoworld ORR", "London", 123};
10
11
int main() {
12
    Employee john{ "John Doe", &LondonOffice };
13
    Employee jane{ "Jane Doe", &LondonOffice };
14
    Employee jack{ "jack Doe", &BangaluruOffice };
15
    return EXIT_SUCCESS;
16
}

The above solution is suitable for our use case, but sometimes we want to customize that office address. And when it comes to pointers and references and any sort of indirection, ordinary copying using operator equals quite simply does not work.
 
A standard way to implement this is by implementing the copy constructor.

Prototype Factory

In the previous example of the Prototype Design Pattern, we basically had a global object for office addresses and used their address for creating prototypes.
 
Now, this isn't particularly convenient to the consumers of your API because you might want to give them a prototype to work with. And you should explicit enough in terms of letting people know there is the only a unified way by which they create instances from a prototype and so that they cannot make individual instances by themselves.
 
In this case, what you would build is off-course is a Prototype Factory:
C++
xxxxxxxxxx
1
64
 
1
struct Office {
2
    string      m_street;
3
    string      m_city;
4
    int32_t     m_cubical;
5
};
6
7
class Employee {
8
    string      m_name;
9
    Office*     m_office;
10
11
    // Private constructor, so direct instance can not be created except for `class EmployeeFactory`
12
    Employee(string n, Office *o) : m_name(n), m_office(o) {}
13
    friend class EmployeeFactory;
14
15
public:
16
    Employee(const Employee &rhs) : m_name{rhs.m_name}, m_office{new Office{*rhs.m_office}} 
17
    { }
18
19
    Employee& operator=(const Employee &rhs) {
20
        if (this == &rhs) return *this;
21
        m_name = rhs.m_name;
22
        m_office = new Office{*rhs.m_office};
23
        return *this;
24
    }
25
26
    friend ostream &operator<<(ostream &os, const Employee &o) {
27
        return os << o.m_name << " works at " 
28
        << o.m_office->m_street << " " << o.m_office->m_city << " seats @" << o.m_office->m_cubical;
29
    }
30
};
31
32
class EmployeeFactory {
33
    static Employee     main;
34
    static Employee     aux;
35
    static unique_ptr<Employee> NewEmployee(string n, int32_t c, Employee &proto) {
36
        auto e = make_unique<Employee>(proto);
37
        e->m_name = n;
38
        e->m_office->m_cubical = c;
39
        return e;
40
    }
41
42
public:
43
    static unique_ptr<Employee> NewMainOfficeEmployee(string name, int32_t cubical) {
44
        return NewEmployee(name, cubical, main);
45
    }
46
    static unique_ptr<Employee> NewAuxOfficeEmployee(string name, int32_t cubical) {
47
        return NewEmployee(name, cubical, aux);
48
    }
49
};
50
51
// Static Member Initialization 
52
Employee EmployeeFactory::main{"", new Office{"123 East Dr", "London", 123}};
53
Employee EmployeeFactory::aux{"", new Office{"RMZ Ecoworld ORR", "London", 123}};
54
55
int main() {
56
    auto jane = EmployeeFactory::NewMainOfficeEmployee("Jane Doe", 125);
57
    auto jack = EmployeeFactory::NewAuxOfficeEmployee("jack Doe", 123);
58
    cout << *jane << endl << *jack << endl;
59
    return EXIT_SUCCESS;
60
}
61
/*
62
Jane Doe works at 123 East Dr London seats @125
63
jack Doe works at RMZ Ecoworld ORR London seats @123
64
*/

The subtle thing to note here is the private constructor of Employee and friend EmployeeFactory. This is how we enforce the client/API-user to create an instance of Employee only through EmployeeFactory .

Leveraging Prototype Design Pattern to Implement Virtual Copy Constructor

In C++, Prototype is also useful to create a copy of an object without knowing its concrete type. Hence, it is also known as Virtual Copy Constructor.

Problem

C++ has the support of polymorphic object destruction using its base class' virtual destructor. Equivalent support for creation and copying of objects is missing as С++ doesn't support virtual constructor and virtual copy constructors.
 
Moreover, you can't create an object unless you know its static type because the compiler must know the amount of space it needs to allocate. For the same reason, copy of an object also requires its type to known at compile-time.
 
Consider the following example as problem statement:
C++
xxxxxxxxxx
1
17
 
1
struct animal {
2
    virtual ~animal(){ cout<<"~animal\n"; }
3
};
4
5
struct dog : animal {
6
    ~dog(){ cout<<"~dog\n"; }
7
};
8
9
struct cat : animal {
10
    ~cat(){ cout<<"~cat\n"; }
11
};
12
13
void who_am_i(animal *who) { // not sure whether dog would be passed here or cat
14
    // How to `create` the object of same type i.e. pointed by who ?
15
    // How to `copy` object of same type i.e. pointed by who ?
16
    delete who; // you can delete appropriate object pointed by who, thanks to virtual destructor
17
}

Just don't think of dynamic_cast<>, it's a code smell.

Solution

The Virtual Constructor/Copy-Constructor technique allows polymorphic creation and copying of objects in C++ by delegating the act of creation and copying the object to the derived class through the use of virtual methods.
 
The following code not only implements virtual copy constructor (i.e. clone()) but also implement virtual constructor(i.e. create()).
C++
 




xxxxxxxxxx
1
21


 
1
struct animal {
2
    virtual ~animal() = default;
3
    virtual std::unique_ptr<animal> create() = 0;
4
    virtual std::unique_ptr<animal> clone() = 0;
5
};
6

           
7
struct dog : animal {
8
    std::unique_ptr<animal> create() { return std::make_unique<dog>(); }
9
    std::unique_ptr<animal> clone() { return std::make_unique<dog>(*this); }
10
};
11

           
12
struct cat : animal {
13
    std::unique_ptr<animal> create() { return std::make_unique<cat>(); }
14
    std::unique_ptr<animal> clone() { return std::make_unique<cat>(*this); }
15
};
16

           
17
void who_am_i(animal *who) {
18
    auto new_who = who->create();// `create` the object of same type i.e. pointed by who ?
19
    auto duplicate_who = who->clone(); // `copy` object of same type i.e. pointed by who ?    
20
    delete who; 
21
}



Benefits of Prototype Design Pattern

  1.  Prototypes are useful when the object instantiation is expensive, thus avoid expensive "creation from scratch", and support cheap cloning of a pre-initialized prototype.
  2.  The prototype provides the flexibility to create highly dynamic systems by defining new behavior through object composition and specifying values for an object's data members at the time of instantiation unlike defining new classes.
  3.  You can simplify the system by producing complex objects more conveniently.
  4.  Especially in C++, Prototype Design Pattern is helpful in creating copy of an object without even knowing its type.

Summary by FAQs

Q. What's the point of using the Prototype Design Pattern?

  • To create an object rapidly based on cloning a pre-configured object.
  • Useful to remove a bunch of boilerplate code.
  • Handy while working with an object without knowing its type.
  • Prototype Design Pattern is an obvious choice while you are working with the Command Design Pattern. For example, in HTTP request most of the time header & footer content remains the same, what changes are data. In such a scenario, you should not create an object from scratch. Rather leverage Prototype Design Pattern.

Q. Is the Prototype Design Pattern Really Just Clone?

It isn't if you combine it with the Factory Design Pattern.

Q. Prototype design pattern to be used when creation is costly, but we do create in the clone.

You must be wondering that in Prototype Factory we show above, we are creating instances in the copy constructor. Isn't that expensive. Yes, it is. But just think about HTTP request, its header consist version, encoding type, content type, server-type, etc. Initially, you need a find out these parameters using respective function calls. But once you got these, these are not going to change until connection closed. So there is no point in doing function calls to extract these params over & over. What cost us here is not parameters but their functions to extract value.

Prototype Design Object (computer science)

Published at DZone with permission of Vishal Chovatiya. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Prototype Pattern in JavaScript
  • Design Pattern: What You Need to Know to Improve Your Code Effectively
  • Accessibility Testing for Digital Products
  • Object Relational Behavioral Design Patterns in Java

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!