Thanks to visit codestin.com
Credit goes to www.tutorialspoint.com

Prototype Design Pattern in C++



The Prototype Pattern is a part of the Creational Design Patterns family.

Consider a scenario where you have an object and your friend asks you to create its replica. How would you do that? You are probably thinking of creating a new object and then copying the values from the existing object to the new object. That is some good thinking! But, what if the existing objects have some fields that are private and not visible outside the class. And also, you would have to know the class of the existing object to create a new object of the same class, which makes your code dependent on the class of the object. In such cases, we need to use the Prototype Pattern.

The Prototype Pattern allows us to make copies from an actual object. It declares a common interface for all objects and those objects support cloning themselves. This means the object itself is responsible for creating a copy of itself. The client code can use the common interface to clone the object without knowing the class of the object. This interface has only one method, which is clone(). The clone() method is responsible for creating a copy of the object and returning it to the client code.

Whichever object that supports cloning is called a prototype. If your objects have too many feilds and many different types of configurations, then you can just clone them.

In the following image, an artist makes three different types of paintings. If someone wants a copy of one of the paintings, the artist can just clone the painting and give it to the person. The artist doesn't need to create a new painting from scratch. This is how the Prototype Pattern works.

Prototype Pattern in C++

Illustration of Prototype Pattern

Following is the illustration of Prototype Pattern. In the below image, we have a Client that wants to create a copy of an existing Prototype object. The Client uses the clone() method of the Prototype to create a copy of the object without knowing the class of the object.

Prototype Pattern in C++

Implementation of Prototype Pattern in C++

We can implement the Prototype Pattern in the following two ways −

  • Basic Prototype Pattern − This is the simple way without any registry.
  • Prototype Pattern with Registry − This approach uses a registry to keep track of all prototypes.

1. Basic Prototype Pattern in C++

The following illustration shows the steps for implementing the Basic Prototype Pattern in C++

Basic Prototype Pattern in C++

Example of Basic Prototype Pattern in C++

Let's implement a simple example of Basic Prototype Pattern in C++

Here we will take an example of a virtual machine class that supports cloning itself. The client will use the clone() method of the VirtualMachine class to create a copy of the object.

#include <iostream>
#include <memory>
#include <string>
using namespace std;

// Prototype Interface
class VirtualMachine {
   public:
      virtual unique_ptr<VirtualMachine> clone() const = 0;
      virtual void showConfig() const = 0;
      virtual ~VirtualMachine() = default;
};

// Concrete Prototype: LinuxVM
class LinuxVM : public VirtualMachine {
   string os;
   int ram;
   int cpu;
   public:
      LinuxVM(string os, int ram, int cpu) : os(move(os)), ram(ram), cpu(cpu) {}

      unique_ptr<VirtualMachine> clone() const override {
         cout << "Cloning Linux VM..." << endl;
         return make_unique<LinuxVM>(*this); // deep copy
      }

      void showConfig() const override {
         cout << "Linux VM - OS: " << os 
              << ", RAM: " << ram << "GB, CPU: " << cpu << " cores" << endl;
      }
};

// Concrete Prototype: WindowsVM
class WindowsVM : public VirtualMachine {
   string os;
   int ram;
   int cpu;
   public:
      WindowsVM(string os, int ram, int cpu) : os(move(os)), ram(ram), cpu(cpu) {}

      unique_ptr<VirtualMachine> clone() const override {
         cout << "Cloning Windows VM..." << endl;   
         return make_unique<WindowsVM>(*this);
      }

      void showConfig() const override {
         cout << "Windows VM - OS: " << os 
              << ", RAM: " << ram << "GB, CPU: " << cpu << " cores" << endl;
      }
};

// Client code
int main() {
   // Create prototypes
   unique_ptr<VirtualMachine> linuxVM = make_unique<LinuxVM>("Ubuntu 20.04", 8, 4);
   unique_ptr<VirtualMachine> windowsVM = make_unique<WindowsVM>("Windows 10", 16, 8);

   //original objects
   cout << "Original VMs:" << endl;
   linuxVM->showConfig();
   windowsVM->showConfig();
   // Clone objects safely
   cout << "\nCloning VMs:" << endl;
   auto clonedLinuxVM = linuxVM->clone();
   auto clonedWindowsVM = windowsVM->clone();

   // Show configurations
   cout << "\nCloned VMs:" << endl;
   clonedLinuxVM->showConfig();
   clonedWindowsVM->showConfig();

   return 0;
}

Following is the output of the above program −

Original VMs:
Linux VM - OS: Ubuntu 20.04, RAM: 8GB, CPU: 4 cores
Windows VM - OS: Windows 10, RAM: 16GB, CPU: 8 cores

Cloning VMs:
Cloning Linux VM...
Cloning Windows VM...

Cloned VMs:
Linux VM - OS: Ubuntu 20.04, RAM: 8GB, CPU: 4 cores
Windows VM - OS: Windows 10, RAM: 16GB, CPU: 8 cores

2. Prototype Pattern with Registry in C++

The following illustration shows the steps for implementing the Prototype Pattern with Registry in C++

Prototype Pattern with Registry in C++

Example of Prototype Pattern with Registry in C++

Let's implement a simple example of Prototype Pattern with Registry in C++:

Here we will take an example of making UI elements like Button and TextBox. We will create a Prototype Registry to keep track of all prototypes. The Client will use the registry to get the prototype and then clone whatever it wants.

#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
using namespace std;
// Prototype Interface
class UIElement {
   public:
      virtual unique_ptr<UIElement> clone() const = 0;
      virtual void render() const = 0;
      virtual ~UIElement() = default;
};

// Concrete Prototype: Button
class Button : public UIElement {
   string label;
   public:
      Button(string label) : label(move(label)) {}

      unique_ptr<UIElement> clone() const override {
         cout << "Cloning Button..." << endl;
         return make_unique<Button>(*this); // deep copy
      }

      void render() const override {
         cout << "Rendering Button: " << label << endl;
      }
};

// Concrete Prototype: TextBox
class TextBox : public UIElement {
   string text;
   public:
      TextBox(string text) : text(move(text)) {}

      unique_ptr<UIElement> clone() const override {
         cout << "Cloning TextBox..." << endl;   
         return make_unique<TextBox>(*this);
      }

      void render() const override {
         cout << "Rendering TextBox: " << text << endl;
      }
};
// Prototype Registry
class PrototypeRegistry {
   unordered_map<string, unique_ptr<UIElement>> prototypes;
   public:
      void registerPrototype(const string& name, unique_ptr<UIElement> prototype) {
         prototypes[name] = move(prototype);
      }

      unique_ptr<UIElement> getPrototype(const string& name) const {
         auto it = prototypes.find(name);
         if (it != prototypes.end()) {
            return it->second->clone();
         }
         return nullptr;
      }
};
// Client code
int main() {
   // Create registry and register prototypes
   PrototypeRegistry registry;
   registry.registerPrototype("Button", make_unique<Button>("Submit"));
   registry.registerPrototype("TextBox", make_unique<TextBox>("Enter your name"));

   // Clone objects safely using the registry
   cout << "Cloning UI Elements:" << endl;
   auto clonedButton = registry.getPrototype("Button");
   auto clonedTextBox = registry.getPrototype("TextBox");

   // Show configurations
   cout << "\nCloned UI Elements:" << endl;
   if (clonedButton) clonedButton->render();
   if (clonedTextBox) clonedTextBox->render();

   return 0;
}

Following is the output of the above program −

Cloning UI Elements:
Cloning Button...
Cloning TextBox...

Cloned UI Elements:
Rendering Button: Submit
Rendering TextBox: Enter your name

Key Properties of Prototype Pattern

Following are the key properties of the Prototype Pattern

  • Cloning, you can copy of existing objects. You don't need to create new objects from scratch.
  • Decoupling, the client code is decoupled from the class of the object.
  • Dynamic, you can add new prototypes at runtime.
  • Can Create More than one type, you can create different types of objects using the same interface.
  • Memory Usage, you can save memory by reusing existing objects instead of creating new ones.

Conclusion

In this chapter, we learned what is the Prototype Pattern and how to implement it in C++. We also saw two different ways of implementing the Prototype Pattern: Basic Prototype Pattern and Prototype Pattern with Registry. The Prototype Pattern is a powerful design pattern that allows us to create copies of existing objects without knowing their class. It helps in reducing memory usage and decoupling the client code from the class of the object.

Advertisements