본문 바로가기

c c++ mfc

디자인패턴 - 프로토타입(Prototype)

반응형

프로토타입 패턴(Prototype Pattern)은 객체 생성의 구체적인 클래스를 명시하지 않고, 기존 객체를 복사하여 새로운 객체를 생성하는 방식입니다. 이 패턴은 객체 생성 비용이 큰 경우, 또는 같은 구조의 객체를 여러 개 만들어야 할 때 유용합니다. 프로토타입 패턴은 주로 두 가지 주요 개념으로 구성됩니다: 복사(클론)와 인터페이스.

Prototype

주요 특징

객체 복사: 기존 객체를 복사하여 새로운 객체를 생성할 수 있습니다. 이 방식은 객체의 생성 비용이 클 때 유용합니다.
유연성: 새로운 객체를 생성할 때, 기존 객체를 기반으로 할 수 있어 유연성이 높습니다.
클라이언트 코드 간소화: 클라이언트는 객체의 구체적인 클래스에 의존하지 않고, 인터페이스를 통해 객체를 복사할 수 있습니다.

 

C++ 코드 예시

아래는 프로토타입 패턴을 사용하여 도형 객체를 복사하는 예시입니다.

#include <iostream>
#include <string>
#include <unordered_map>

// 프로토타입 인터페이스
class Shape {
public:
    virtual Shape* clone() const = 0; // 클론 메서드
    virtual void draw() const = 0;     // 도형 그리기
    virtual ~Shape() {}
};

// 구체 제품 클래스: 원
class Circle : public Shape {
public:
    Circle() {}
    Circle(const Circle&) {} // 복사 생성자

    Shape* clone() const override {
        return new Circle(*this); // 현재 객체를 복사하여 새 객체 생성
    }

    void draw() const override {
        std::cout << "Drawing Circle" << std::endl;
    }
};

// 구체 제품 클래스: 사각형
class Square : public Shape {
public:
    Square() {}
    Square(const Square&) {} // 복사 생성자

    Shape* clone() const override {
        return new Square(*this); // 현재 객체를 복사하여 새 객체 생성
    }

    void draw() const override {
        std::cout << "Drawing Square" << std::endl;
    }
};

// 클라이언트 코드
int main() {
    // 원과 사각형 객체 생성
    Shape* circle = new Circle();
    Shape* square = new Square();

    // 객체 복사
    Shape* circleClone = circle->clone();
    Shape* squareClone = square->clone();

    // 그리기
    circle->draw();          // "Drawing Circle"
    square->draw();         // "Drawing Square"
    circleClone->draw();    // "Drawing Circle"
    squareClone->draw();     // "Drawing Square"

    // 메모리 해제
    delete circle;
    delete square;
    delete circleClone;
    delete squareClone;

    return 0;
}

설명

1. 프로토타입 인터페이스: Shape 클래스는 clone() 메서드와 draw() 메서드를 정의합니다. clone() 메서드는 객체를 복사하는 역할을 합니다.

2. 구체 제품 클래스:

  • Circle 클래스와 Square 클래스는 Shape 인터페이스를 구현합니다. 각각의 clone() 메서드는 현재 객체를 복사하여 새로운 객체를 생성합니다.
  • draw() 메서드는 각 도형의 정보를 출력합니다.

3. 클라이언트 코드:
main 함수에서는 Circle과 Square 객체를 생성한 후, clone() 메서드를 호출하여 복사본을 생성합니다.
각 객체의 draw() 메서드를 호출하여 도형을 그리는 메시지를 출력합니다.

위 코드를 실행한 결과는 다음과 같다.

Drawing Circle
Drawing Square
Drawing Circle
Drawing Square

실행 과정 설명:

1. 객체 생성:

  • Circle 객체와 Square 객체가 각각 생성됩니다.
    2. 객체 복사:
  • circle->clone() 메서드를 호출하여 Circle 객체의 복사본(circleClone)이 생성됩니다.
  • square->clone() 메서드를 호출하여 Square 객체의 복사본(squareClone)이 생성됩니다.
    3. 도형 그리기:
  • circle->draw() 호출: "Drawing Circle"이 출력됩니다.
  • square->draw() 호출: "Drawing Square"가 출력됩니다.
  • circleClone->draw() 호출: 복사된 원의 draw() 메서드가 호출되어 "Drawing Circle"이 출력됩니다.
  • squareClone->draw() 호출: 복사된 사각형의 draw() 메서드가 호출되어 "Drawing Square"가 출력됩니다.

다른 예시코드 하나 더 갑니다.

#include <iostream>
#include <memory>
#include <string>

// Prototype 인터페이스
class Shape {
public:
    virtual ~Shape() {}
    virtual std::unique_ptr<Shape> clone() const = 0;
    virtual void draw() const = 0;
};

// Concrete Prototype: Circle
class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r) : radius(r) {}

    // 복제 메서드 구현
    std::unique_ptr<Shape> clone() const override {
        return std::make_unique<Circle>(*this);
    }

    void draw() const override {
        std::cout << "Drawing a Circle with radius " << radius << std::endl;
    }
};

// Concrete Prototype: Square
class Square : public Shape {
private:
    double side;

public:
    Square(double s) : side(s) {}

    // 복제 메서드 구현
    std::unique_ptr<Shape> clone() const override {
        return std::make_unique<Square>(*this);
    }

    void draw() const override {
        std::cout << "Drawing a Square with side " << side << std::endl;
    }
};

int main() {
    // 원형 객체 생성
    std::unique_ptr<Shape> circlePrototype = std::make_unique<Circle>(5.0);
    std::unique_ptr<Shape> squarePrototype = std::make_unique<Square>(7.0);

    // 원형 객체 복제 및 사용
    auto clonedCircle1 = circlePrototype->clone();
    clonedCircle1->draw();

    auto clonedCircle2 = circlePrototype->clone();
    clonedCircle2->draw();

    auto clonedSquare1 = squarePrototype->clone();
    clonedSquare1->draw();

    auto clonedSquare2 = squarePrototype->clone();
    clonedSquare2->draw();

    return 0;
}

1. Prototype 인터페이스 (Shape)

class Shape {
public:
    virtual ~Shape() {}
    virtual std::unique_ptr<Shape> clone() const = 0;
    virtual void draw() const = 0;
};
  • Shape 클래스는 추상 클래스로, 모든 도형이 가져야 할 공통 인터페이스를 정의합니다.
  • clone() 메서드는 객체를 복제하는 역할을 합니다. 이는 순수 가상 함수로 선언되어 있어, 이를 상속받는 클래스가 반드시 구현해야 합니다.
  • draw() 메서드는 도형을 그리는 기능을 정의하며, 역시 순수 가상 함수입니다.

2. Concrete Prototype: Circle

class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r) : radius(r) {}

    std::unique_ptr<Shape> clone() const override {
        return std::make_unique<Circle>(*this);
    }

    void draw() const override {
        std::cout << "Drawing a Circle with radius " << radius << std::endl;
    }
};
  • Circle 클래스는 Shape 인터페이스를 구현한 구체적인 프로토타입 클래스입니다.
  • 생성자에서 원의 반지름(radius) 값을 초기화합니다.
  • 복제 메서드 (clone):
    • clone() 메서드는 현재 객체(*this)를 복제하여 새로운 객체를 반환합니다.
    • std::make_unique(*this)를 사용하여 깊은 복사를 수행합니다.
  • 그리기 메서드 (draw):
    • 원의 반지름 값을 출력하며 원을 "그리는" 역할을 합니다.

3. Concrete Prototype: Square

class Square : public Shape {
private:
    double side;

public:
    Square(double s) : side(s) {}

    std::unique_ptr<Shape> clone() const override {
        return std::make_unique<Square>(*this);
    }

    void draw() const override {
        std::cout << "Drawing a Square with side " << side << std::endl;
    }
};
  • Square 클래스는 Shape 인터페이스를 구현한 또 다른 구체적인 프로토타입 클래스입니다.
  • 생성자에서 정사각형의 한 변(side) 길이를 초기화합니다.
  • 복제 메서드 (clone):
    • 현재 객체를 복제하여 새로운 객체를 반환합니다.
    • std::make_unique(*this)를 사용하여 깊은 복사를 수행합니다.
  • 그리기 메서드 (draw):
    • 정사각형의 한 변 길이를 출력하며 정사각형을 "그리는" 역할을 합니다.

4. Client 코드

int main() {
    // 원형 객체 생성
    std::unique_ptr<Shape> circlePrototype = std::make_unique<Circle>(5.0);
    std::unique_ptr<Shape> squarePrototype = std::make_unique<Square>(7.0);

    // 원형 객체 복제 및 사용
    auto clonedCircle1 = circlePrototype->clone();
    clonedCircle1->draw();

    auto clonedCircle2 = circlePrototype->clone();
    clonedCircle2->draw();

    auto clonedSquare1 = squarePrototype->clone();
    clonedSquare1->draw();

    auto clonedSquare2 = squarePrototype->clone();
    clonedSquare2->draw();

    return 0;
}

주요 흐름:

  1. 원형 객체 생성:
    • 클라이언트는 먼저 원형 객체(circlePrototype, squarePrototype)를 생성합니다.
    • 각각 반지름이 5인 원과 한 변 길이가 7인 정사각형으로 초기화됩니다.
  2. 복제 및 사용:
    • 클라이언트는 원형 객체의 clone() 메서드를 호출하여 새로운 객체를 복제합니다.
    • 복제된 객체(clonedCircle1, clonedCircle2, clonedSquare1, clonedSquare2)는 각각 독립적인 상태를 가지며, 이를 바로 사용할 수 있습니다.
    • 복제된 객체에서 draw() 메서드를 호출하여 도형 정보를 출력합니다.

실행 결과 분석

코드 실행 시 다음과 같은 출력이 나타납니다:

Drawing a Circle with radius 5
Drawing a Circle with radius 5
Drawing a Square with side 7
Drawing a Square with side 7

설명:

  • 첫 번째와 두 번째 줄은 원형 객체(circlePrototype)에서 복제된 두 개의 원(clonedCircle1, clonedCircle2)을 그린 결과입니다. 두 원은 동일한 반지름(5)을 가집니다.
  • 세 번째와 네 번째 줄은 정사각형 원형 객체(squarePrototype)에서 복제된 두 개의 정사각형(clonedSquare1, clonedSquare2)을 그린 결과입니다. 두 정사각형은 동일한 변 길이(7)를 가집니다.

 

장점

객체 생성 비용이 클 때, 기존 객체를 복사하여 새로운 객체를 생성함으로써 성능을 개선할 수 있습니다.
클라이언트는 객체의 구체적인 클래스에 의존하지 않고, 인터페이스를 통해 객체를 복사할 수 있습니다.

단점

복사할 객체가 복잡한 상태를 가지고 있을 경우, 깊은 복사가 필요할 수 있어 구현이 복잡해질 수 있습니다.
객체의 상태를 복사해야 하므로, 필요한 경우 모든 속성을 수동으로 복사해야 할 수 있습니다.
프로토타입 패턴은 객체 생성의 유연성을 높이고, 복잡한 객체를 쉽게 관리할 수 있도록 도와주는 유용한 디자인 패턴입니다.

반응형