C++ Prototype 패턴

기존 객체를 복제하여 새 객체를 생성하는 Prototype 패턴입니다. 가상 clone 메서드, CRTP 기반 자동 복제, 그리고 Prototype Registry를 포함합니다.

Gist
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <stdexcept>

// ─────────────────────────────────────────────
// 방법 1: 가상 clone() 메서드를 통한 Prototype
// 기반 클래스에서 인터페이스를 선언
// ─────────────────────────────────────────────
class Shape {
public:
    explicit Shape(std::string color) : color_(std::move(color)) {}
    virtual ~Shape() = default;

    // 핵심: 자기 자신의 복사본을 동적으로 생성
    virtual std::unique_ptr<Shape> clone() const = 0;
    virtual void draw() const = 0;

    void setColor(const std::string& c) { color_ = c; }
    const std::string& color() const    { return color_; }

protected:
    std::string color_;
};

// ─────────────────────────────────────────────
// 방법 2: CRTP(Curiously Recurring Template Pattern)
// 서브클래스마다 clone()을 반복 구현하지 않아도 됨
// ─────────────────────────────────────────────
template <typename Derived>
class CloneableShape : public Shape {
public:
    using Shape::Shape;  // 부모 생성자 상속

    // CRTP: Derived 타입으로 복제 — 오버라이드 불필요
    std::unique_ptr<Shape> clone() const override {
        return std::make_unique<Derived>(static_cast<const Derived&>(*this));
    }
};

// ─────────────────────────────────────────────
// ConcretePrototype: CRTP 기반 구체 도형들
// ─────────────────────────────────────────────
class Circle final : public CloneableShape<Circle> {
public:
    Circle(std::string color, double radius)
        : CloneableShape(std::move(color)), radius_(radius) {}

    void draw() const override {
        std::cout << "원 [색상: " << color_
                  << ", 반지름: " << radius_ << "]\n";
    }

    void setRadius(double r) { radius_ = r; }
    double radius() const    { return radius_; }

private:
    double radius_;
};

class Rectangle final : public CloneableShape<Rectangle> {
public:
    Rectangle(std::string color, double w, double h)
        : CloneableShape(std::move(color)), width_(w), height_(h) {}

    void draw() const override {
        std::cout << "사각형 [색상: " << color_
                  << ", 너비: " << width_
                  << ", 높이: " << height_ << "]\n";
    }

    void setSize(double w, double h) { width_ = w; height_ = h; }

private:
    double width_, height_;
};

class Triangle final : public CloneableShape<Triangle> {
public:
    Triangle(std::string color, double base, double height)
        : CloneableShape(std::move(color)), base_(base), height_(height) {}

    void draw() const override {
        std::cout << "삼각형 [색상: " << color_
                  << ", 밑변: " << base_
                  << ", 높이: " << height_ << "]\n";
    }

private:
    double base_, height_;
};

// ─────────────────────────────────────────────
// Prototype Registry: 미리 등록된 원형을 이름으로 조회·복제
// 매번 new로 생성하지 않고 등록된 원형을 복제해서 사용
// ─────────────────────────────────────────────
class ShapeRegistry {
public:
    // 원형 등록 (이름 → 원형 객체)
    void registerShape(const std::string& name, std::unique_ptr<Shape> proto) {
        registry_[name] = std::move(proto);
    }

    // 등록된 원형의 복사본 반환
    std::unique_ptr<Shape> create(const std::string& name) const {
        auto it = registry_.find(name);
        if (it == registry_.end()) {
            throw std::out_of_range("등록되지 않은 원형: " + name);
        }
        return it->second->clone();  // 원형을 복제하여 반환
    }

    void listAll() const {
        std::cout << "등록된 원형 목록:\n";
        for (const auto& [name, _] : registry_) {
            std::cout << "  - " << name << "\n";
        }
    }

private:
    std::unordered_map<std::string, std::unique_ptr<Shape>> registry_;
};

// ─────────────────────────────────────────────
// 사용 예시
// ─────────────────────────────────────────────
int main() {
    std::cout << "=== 직접 clone() 사용 ===\n";
    // 원본 생성 후 복제 → 독립적인 객체
    auto original = std::make_unique<Circle>("빨간색", 5.0);
    auto cloned   = original->clone();

    original->draw();
    cloned->draw();

    // 복제본 수정이 원본에 영향 없음을 확인
    static_cast<Circle*>(cloned.get())->setColor("파란색");
    std::cout << "복제본 수정 후:\n";
    original->draw();  // 원본 불변
    cloned->draw();    // 복제본만 변경

    std::cout << "\n=== Prototype Registry 사용 ===\n";
    ShapeRegistry registry;

    // 원형 등록
    registry.registerShape("기본-원",    std::make_unique<Circle>("회색", 10.0));
    registry.registerShape("기본-사각형", std::make_unique<Rectangle>("회색", 100.0, 50.0));
    registry.registerShape("기본-삼각형", std::make_unique<Triangle>("회색", 60.0, 40.0));
    registry.listAll();

    // 원형으로부터 새 인스턴스 생성 후 커스터마이징
    std::cout << "\n복제 후 커스터마이징:\n";
    auto myCircle = registry.create("기본-원");
    myCircle->setColor("초록색");
    myCircle->draw();

    auto myRect = registry.create("기본-사각형");
    myRect->setColor("노란색");
    myRect->draw();

    return 0;
}