프로토타입 패턴(Prototype Pattern)

  • 객체를 생성하는 방법은 new 키워드를 사용한 방법이 일반적입니다.
    • C++의 스마트 포인터도 내부적으로 new 키워드를 사용합니다.
  • 객체를 생성하는 또 한가지 방법은 객체를 복사(Copy)하는 방법입니다.
  • 프로토타입 패턴은 객체를 복사하는 방법을 사용하여 객체를 생성합니다.

프로토타입 패턴의 구조

  • 프로토타입 패턴은 신규 객체를 생성하지 않고, 기존에 만들어진 객체를 복제합니다.
  • C++에서 객체를 복사하는 방법은 얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)가 있습니다.
  • 프로토타입 패턴은 특별한 구조를 갖는 것이 아니라, 복사를 활용합니다.

프로토타입 패턴을 위한 예제

  • 사용자가 지정한 메세지를 객체가 저장하고 있다가 필요할때 반환하는 클래스를 작성합니다.
class Hello
{
public:
    Hello(const std::string& message)
    {
        m_message = message;
    }

    void setMessage(const std::string& message)
    {
        m_message = message;
    }

    const std::string& getMessage()
    {
        return m_message;
    }

private:
    std::string m_message;
};

얕은 복사(Shallow Copy)

  • 객체 복사는 일반적인 대입연산자(=)를 사용합니다.
  • 얕은 복사는 객체를 복사하더라도, 객체가 갖고 있는 변수들을 공유합니다.
  • 변수를 공유한다는 것은 불필요한 메모리를 할당하지 않고, 자원을 절약하는 방법입니다.

얕은 복사 예제 실행 코드

  • 간단한 예제를 작성하고 실행 결과를 살펴봅니다.
int main(const int argc, const char* argv[])
{
    std::shared_ptr<Hello> korea = std::make_shared<Hello>("Hello Korea");
    korea->setMessage("Hello Korea");
    std::cout << "Original message: " << korea->getMessage() << std::endl;

    // Shallow copy
    std::shared_ptr<Hello> english = korea;
    english->setMessage("Hello English");
    std::cout << "After shallow copied original message: " << korea->getMessage() << std::endl;
    std::cout << "english message: " << english->getMessage() << std::endl;

    return 0;
}
  • 실행 결과
Original message: Hello Korea
After shallow copied original message: Hello English
english message: Hello English
  • 대입연산자(=)로 객체를 복사하고 setMessage() 메서드로 프로퍼티의 값을 변경했습니다.
  • 원본 객체인 korea의 프로퍼티 또한 변경된 것을 확인할 수 있습니다.
  • 이처럼 얕은 복사는 불필요한 메모리의 확장을 방지합니다.

깊은 복사(Deep Copy)

  • 깊은 복사는 객체를 복제할 경우 내부 프로퍼티를 공유하지 않습니다.
  • 프로토타입 인터페이스 클래스를 설계합니다.
  • 복제한 대상 객체들의 공통 인터페이스인 setMessage()getMessage()를 추가로 선언하였습니다.
  • clone() 메서드의 반환형은 구현하는 타입에 따라 다르게 설정할수 있습니다.
class Prototype
{
public:
    virtual std::shared_ptr<Prototype> clone() const = 0;
    virtual void setMessage(const std::string& message) = 0;
    virtual	const std::string& getMessage() = 0;
};
  • 이어서 Prototype을 상속하는 클래스인 Hello도 다시 수정합니다.
class Hello : 
    public Prototype
{
public:
    Hello(const std::string& message)
    {
        m_message = message;
    }

    void setMessage(const std::string& message)
    {
        m_message = message;
    }

    const std::string& getMessage()
    {
        return m_message;
    }

    std::shared_ptr<Prototype> clone() const override
    {
        return std::make_shared<Hello>("");
    }

private:
    std::string m_message;
};

깊은 복사 예제 실행 코드

  • 예제 코드를 작성해보고 얕은 복사와 결과가 어떻게 다른지 확인합니다.
int main(const int argc, const char* argv[])
{
    std::shared_ptr<Prototype> korea = std::make_shared<Hello>("Hello Korea");
    korea->setMessage("Hello Korea");
    std::cout << "Original message: " << korea->getMessage() << std::endl;

    // deep copy
    auto english = korea->clone();
    english->setMessage("Hello English");
    std::cout << "After shallow copied original message: " << korea->getMessage() << std::endl;
    std::cout << "english message: " << english->getMessage() << std::endl;

    return 0;
}
  • 실행 결과
Original message: Hello Korea
After shallow copied original message: Hello Korea
english message: Hello English
  • clone()을 사용할 경우 원본 객체의 프로퍼티가 변경되지 않습니다.
  • 이는 원본 객체와 복제한 객체가 갖는 프로퍼티가 다른 공간에 메모리가 할당되었음을 의미합니다.