프로그래밍 일반/디자인 패턴

메멘토 패턴(Memento Pattern)

지노윈 2020. 3. 4. 21:37
반응형

커맨드 패턴에서, 시스템의 모든 변경 이력을 기록해 과거의 어떤 지점으로든 상태를 되돌릴 수 있는 내용을 살펴 보았습니다.

모든 임의의 과거가 아니라 필요한 때 특정 시점으로 되돌릴 수만 있으면 충분한 경우가 있습니다.

메멘토 패턴은 바로 이런 경우 이며 특정 시점의 시스템 상태를 전용 객체에 저장하여 리턴합니다.

이렇게 저장된 객체를 이용하여 저장되어 있던 상태로 되돌릴 수 있습니다.

은행 계좌

다음은 BankAccount 클래스의 정의입니다.

class BankAccount
{
  int balance = 0;
public:
  explicit BankAccount(const int balance)
    : balance(balance)
  {
  }
 
  Memento deposit(int amount)   // 예금 처리후 메멘토 객체를 반환
  {
    balance += amount;
    return { balance };
  }
 
  void restore(const Memento& m)
  {
    balance = m.balance;
  }
};

BankAccount::deposit 함수는 예금을 처리후 메멘토 객체를 반환합니다.

Memento 객체는 계좌를 Memento에 저장된 상태로 되돌릴 때 사용될 수 있습니다.

Memento 클래스는 아래와 같이 쉽게 구현됩니다.

class Memento
{
  int balance;
public:
  Memento(int balance) : balance(balance) {}
  friend class BankAccount;
};

다음과 같이 계좌 상태를 기억해 두었다가 되돌릴 수 있습니다.

BankAccount ba{ 100 };
auto m1 = ba.deposit(50); // 150
auto m2 = ba.deposit(25); // 175
 
// m1로 복구, 잔고 150
ba.restore(m1);
 
// m2로 복구, 잔고 175
ba.restore(m2);

Undo와 Redo

BankAccount에서 생성되는 Memento를 모두 저장하면 어떻게 될까? 이 경우 커맨드 디자인 패턴의 구현에서와 마찬가지로 되돌리기(undo)와 다시하기(redo) 작업이 가능해집니다.

이제 Memento를 이용해서 Undo/Redo 기능의 구현에 대하여 알아봅겠습니다.

class BankAccount2
{
  int balance = 0;
  vector<shared_ptr<Memento>> changes;
  int current;
public:
  explicit BankAccount2(const int balance)
  : balance(balance)
  {
    changes.emplace_back(make_shared<Memento>(balance));
    current = 0;
  }
 
  shared_ptr<Memento> deposit(int amount)
  {
    balance += amount;
    auto m = make_shared<Memento>(balance);
    changes.push_back(m);
    ++current;
    return m;
  }
 
  void restore(const shared_ptr<Memento>& m)
  {
    if (m)
    {
      balance = m->balance;
      changes.push_back(m);
      current = changes.size() - 1;
    }
  }
 
  shared_ptr<Memento> undo()
  {
    if (current > 0)
    {
      --current;
      auto m = changes[current];
      balance = m->balance;
      return m;
    }
    return{};
  }
 
  shared_ptr<Memento> redo()
  {
    if (current + 1 < changes.size())
    {
      ++current;
      auto m = changes[current];
      balance = m->balance;
      return m;
    }
    return{};
  }
};

restore 구현이 조금 달라 졌습니다. 복구 자체도 기록하여 이 동작을 다시 되돌릴 수도 있도록 하였습니다.

undo/redo 구현은 비슷 합니다. current 위치를 변경하여 원하는 위치의 memento를 얻어 옵니다.

이제 사용 예제를 살펴보겠습니다.

 

BankAccount2 ba{ 100 };
ba.deposit(50); // 150
ba.deposit(25); // 175
 
ba.undo();    // 150
ba.undo();    // 100
ba.redo();    // 150
ba.undo();    // 100

 

 

[독서 리뷰] - 모던 C++ 디자인 패턴

 

모던 C++ 디자인 패턴

[객체 지향 프로그래밍/디자인 패턴] - 빌더 패턴(Builder Pattern) [분류 전체보기] - 디자인 패턴 [객체 지향 프로그래밍/디자인 패턴] - 팩토리 패턴(Factory pattern) [객체 지향 프로그래밍/디자인 패턴] -..

devjino.tistory.com

코드 참고 : https://cpp-design-patterns.readthedocs.io/en/latest/
 

Welcome to C++ Design Patterns’s documentation! — C++ Design Patterns 0.0.1 documentation

© Copyright 2018, Hans-J. Schmid Revision 786c83f8.

cpp-design-patterns.readthedocs.io

 

'프로그래밍 일반 > 디자인 패턴' 카테고리의 다른 글

관찰자 패턴(Observer Pattern)  (0) 2020.03.04
Null 객체  (0) 2020.03.04
커맨드 패턴(Command Pattern)  (0) 2020.03.04
중재자 패턴(Mediator Pattern)  (0) 2020.03.04
반복자 패턴(Iterator Pattern)  (0) 2020.03.04