C++ Prototype 패턴
기존 객체를 복제하여 새 객체를 생성하는 Prototype 패턴입니다. 가상 clone 메서드, CRTP 기반 자동 복제, 그리고 Prototype Registry를 포함합니다.
#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;
}