메멘토 패턴(Memento)

  • 메멘토 패턴은 객체의 특정한 상태나, 객체 자체를 저장하여 이전 상태로 복구하는 패턴입니다.
  • 메멘토 패턴의 활용 사례는 대부분 프로그램에 적용되어 있습니다. 바로 이전 상태로 복구(Undo)하는 버튼입니다.
  • 무식한 방법으로는 객체의 프로퍼티를 public으로 지정하여 상태를 저장할 수 있습니다. 하지만 이러한 방법은 객체지향의 캡슐화를 파괴하는 끔찍한 방법입니다.
  • 따라서 객체의 캡슐화를 위반하지 않고 객체의 상태를 저장하는 패턴이 메멘토 패턴입니다.

메멘토 패턴의 구조

  • 메멘토 패턴은 객체를 하나 저장하고 반환하는 Memento 클래스가 존재합니다.
  • Memento 클래스를 사용하여 객체 저장과 복원을 위해 두 가지 인터페이스로 관리 방법을 구분합니다.
    • Originator
      • Originator 클래스는 실제 객체와 Memento 사이의 중간 매개체(constraintSolver) 역할을 수행합니다.
    • CareTaker:
      • CareTaker 클래스는 다수의 Memento 객체를 저장하고, 관리하는 역할을 수행합니다.

메멘토 패턴: 간단한 객체

  • 메멘토 패턴을 학습하기 위해 간단한 문자열을 저장하고, 반환하는 객체를 만들어보겠습니다.
class Hello
{
public:
    Hello(std::string message)
    {
        m_message = message;
    }

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

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

private:
    std::string m_message;
};

메멘토 패턴: 메멘토(Memento) 클래스

  • 메멘토 클래스는 앞서 선언한 간단한 객체 Hello를 저장하고, 반환하는 클래스입니다.
class Memento
{
public:
    Memento(std::shared_ptr<Hello> object)
    {
        m_object = object;
    }

    std::shared_ptr<Hello> getObject() const
    {
        return m_object;
    }

protected:
    std::shared_ptr<Hello> m_object;
};

메멘토 패턴: Originator 클래스

  • Originator 클래스는 Memento 클래스를 생성하고, 객체의 상태를 복원하는 클래스입니다.
  • 내부적으로 Memento 클래스를 호출하여 상태를 복원합니다.
  • C++의 포인터 문제로 setState()에서 객체를 저장하기 위해 깊은 복사를 수행합니다.
class Originator
{
public:
    std::shared_ptr<Memento> createMemento()
    {
        return std::make_shared<Memento>(m_state);
    }

    void restore(std::shared_ptr<Memento> memento)
    {
        m_state = memento->getObject();
    }

    std::shared_ptr<Hello> getState() const
    {
        return m_state;
    }

    void setState(const std::shared_ptr<Hello> state)
    {
        // for deep-copy
        m_state = std::make_shared<Hello>(*state);
    }

protected:
    std::shared_ptr<Hello> m_state;
};

메멘토 패턴: Originator 실행 코드

  • Originator를 사용하여 객체의 상태를 복원하는 코드를 살펴보겠습니다.
  • Originator는 Memento 객체를 내부에서 저장하는 방식이 아닌 외부에서 호출을 통해 복구합니다.
int main(const int argc, const char* argv[])
{
    //Originator
    std::shared_ptr<Originator> originator = std::make_shared<Originator>();

    // first state
    std::shared_ptr<Hello> hello = std::make_shared<Hello>("State1: Hello");
    std::cout << hello->getMessage() << std::endl;

    // save
    originator->setState(hello);
    std::shared_ptr<Memento> memento = originator->createMemento();

    // second state
    hello->setMessage("State2: World!");
    std::cout << hello->getMessage() << std::endl;

    // restore
    originator->restore(memento);
    hello = originator->getState();
    std::cout << hello->getMessage() << std::endl;

    return 0;
}
  • 실행 결과
State1: Hello
State2: World!
State1: Hello

메멘토 패턴: CareTaker 클래스

  • 앞서 Originator 객체는 Memento 객체를 내부적으로 저장하지 않기 때문에 외부에서 호출하는 방식을 사용했습니다.
  • CareTaker 객체는 내부적으로 Memento 객체를 저장합니다.
class CareTaker
{
public:
    void push(std::shared_ptr<Originator> originator)
    {
        std::shared_ptr<Memento> memento = originator->createMemento();
        m_stack.push(memento);
    }

    std::shared_ptr<Hello> undo(std::shared_ptr<Originator> originator)
    {
        std::shared_ptr<Memento> memento = m_stack.top();
        m_stack.pop();

        originator->restore(memento);
        return originator->getState();
    }

protected:
    std::stack<std::shared_ptr<Memento>> m_stack;
};

메멘토 패턴: CareTaker 실행 코드

  • CareTaker 객체를 사용하여 상태를 복구해봅니다.
int main(const int argc, const char* argv[])
{
    // CareTaker
    std::shared_ptr<Originator> originator = std::make_shared<Originator>();
    std::shared_ptr<CareTaker> careTaker = std::make_shared<CareTaker>();

    // first state
    std::shared_ptr<Hello> hello = std::make_shared<Hello>("State1: Hello");
    std::cout << hello->getMessage() << std::endl;

    // save
    originator->setState(hello);
    careTaker->push(originator);

    // second state
    hello->setMessage("State2: World!");
    std::cout << hello->getMessage() << std::endl;
    originator->setState(hello);
    careTaker->push(originator);

    // third state
    hello->setMessage("State3: ABCDEFG");
    std::cout << hello->getMessage() << std::endl;
    originator->setState(hello);
    careTaker->push(originator);

    // undo
    hello = careTaker->undo(originator); // state 3
    std::cout << hello->getMessage() << std::endl;

    hello = careTaker->undo(originator); // state 2
    std::cout << hello->getMessage() << std::endl;

    hello = careTaker->undo(originator); // state 1
    std::cout << hello->getMessage() << std::endl;

    return 0;
}
  • 실행 결과
State1: Hello
State2: World!
State3: ABCDEFG
State3: ABCDEFG
State2: World!
State1: Hello