CreationalC++verifiedVerified
Prototype Pattern in C++
Creates new objects by cloning an existing instance, avoiding the cost of building from scratch.
How to Implement the Prototype Pattern in C++
1Step 1: Define the Prototype interface
class Shape {
public:
virtual ~Shape() = default;
virtual std::unique_ptr<Shape> clone() const = 0;
virtual std::string describe() const = 0;
};2Step 2: Concrete prototypes
class Circle : public Shape {
double radius_;
public:
explicit Circle(double r) : radius_(r) {}
std::unique_ptr<Shape> clone() const override {
return std::make_unique<Circle>(*this);
}
std::string describe() const override {
return "Circle(r=" + std::to_string(radius_) + ")";
}
};
class Rectangle : public Shape {
double w_, h_;
public:
Rectangle(double w, double h) : w_(w), h_(h) {}
std::unique_ptr<Shape> clone() const override {
return std::make_unique<Rectangle>(*this);
}
std::string describe() const override {
return "Rect(" + std::to_string(w_) + "x" + std::to_string(h_) + ")";
}
};3Step 3: Clone objects without knowing their concrete type
int main() {
std::unique_ptr<Shape> original = std::make_unique<Circle>(5.0);
auto copy = original->clone();
std::cout << "Original: " << original->describe() << "\n";
std::cout << "Clone: " << copy->describe() << "\n";
}#include <iostream>
#include <string>
#include <memory>
#include <map>
#include <vector>
#include <format>
#include <concepts>
#include <stdexcept>
// [step] Define the Cloneable concept and prototype interface
template <typename T>
concept Cloneable = requires(const T& t) {
{ t.clone() } -> std::convertible_to<std::unique_ptr<T>>;
};
class IComponent {
public:
virtual ~IComponent() = default;
virtual std::unique_ptr<IComponent> clone() const = 0;
virtual std::string type() const = 0;
virtual std::string serialize() const = 0;
};
static_assert(Cloneable<IComponent>);
// [step] Concrete components with deep copy support
class TextWidget : public IComponent {
std::string text_;
int fontSize_;
std::string color_;
public:
TextWidget(std::string text, int fontSize, std::string color)
: text_(std::move(text)), fontSize_(fontSize), color_(std::move(color)) {}
std::unique_ptr<IComponent> clone() const override {
return std::make_unique<TextWidget>(*this);
}
std::string type() const override { return "TextWidget"; }
std::string serialize() const override {
return std::format("TextWidget('{}', {}px, {})", text_, fontSize_, color_);
}
void setText(const std::string& t) { text_ = t; }
};
class ImageWidget : public IComponent {
std::string src_;
int width_, height_;
public:
ImageWidget(std::string src, int w, int h)
: src_(std::move(src)), width_(w), height_(h) {}
std::unique_ptr<IComponent> clone() const override {
return std::make_unique<ImageWidget>(*this);
}
std::string type() const override { return "ImageWidget"; }
std::string serialize() const override {
return std::format("ImageWidget('{}', {}x{})", src_, width_, height_);
}
};
// [step] Prototype registry for named templates
class PrototypeRegistry {
std::map<std::string, std::unique_ptr<IComponent>> prototypes_;
public:
void registerPrototype(const std::string& name,
std::unique_ptr<IComponent> proto) {
prototypes_[name] = std::move(proto);
}
std::unique_ptr<IComponent> create(const std::string& name) const {
auto it = prototypes_.find(name);
if (it == prototypes_.end())
throw std::runtime_error(
std::format("Prototype '{}' not registered", name));
return it->second->clone();
}
std::vector<std::string> listPrototypes() const {
std::vector<std::string> names;
for (const auto& [name, _] : prototypes_) names.push_back(name);
return names;
}
};
// [step] Deep-copyable composite for complex hierarchies
class CompositeWidget : public IComponent {
std::string name_;
std::vector<std::unique_ptr<IComponent>> children_;
public:
explicit CompositeWidget(std::string name) : name_(std::move(name)) {}
// Deep copy
CompositeWidget(const CompositeWidget& other) : name_(other.name_) {
for (const auto& child : other.children_)
children_.push_back(child->clone());
}
void add(std::unique_ptr<IComponent> child) {
children_.push_back(std::move(child));
}
std::unique_ptr<IComponent> clone() const override {
return std::make_unique<CompositeWidget>(*this);
}
std::string type() const override { return "Composite"; }
std::string serialize() const override {
std::string result = std::format("Composite('{}', [", name_);
for (size_t i = 0; i < children_.size(); ++i) {
if (i > 0) result += ", ";
result += children_[i]->serialize();
}
return result + "])";
}
};
// [step] Demonstrate prototype cloning
int main() {
PrototypeRegistry registry;
registry.registerPrototype("heading",
std::make_unique<TextWidget>("Default Heading", 24, "#000"));
registry.registerPrototype("thumbnail",
std::make_unique<ImageWidget>("placeholder.png", 200, 150));
// Create a composite template
auto card = std::make_unique<CompositeWidget>("Card");
card->add(registry.create("heading"));
card->add(registry.create("thumbnail"));
registry.registerPrototype("card", std::move(card));
// Clone from registry
auto card1 = registry.create("card");
auto card2 = registry.create("card");
std::cout << "Card 1: " << card1->serialize() << "\n";
std::cout << "Card 2: " << card2->serialize() << "\n";
std::cout << "Available: " << registry.listPrototypes().size() << " prototypes\n";
}Prototype Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Prototype Pattern in the Real World
“Think of a cell dividing through mitosis. Instead of building a new cell from raw amino acids, the existing cell duplicates itself — copying its DNA, organelles, and membrane. The result is a fully functional copy produced far faster than assembling one molecule at a time.”