메멘토 패턴

**메멘토 패턴**(memento pattern)은 객체를 이전 상태로 되돌릴 수 있는 기능을 제공하는 소프트웨어 디자인 패턴이다. (롤백을 통한 실행 취소)

메멘토 패턴은 3개의 객체로 구현된다: 오리지네이터(originator), 케어테이커(caretaker), 메멘토(memento). 오리지네이터는 내부 상태를 보유하고 있는 일부 객체이다. 케어테이커는 오리지네이터에 대해 무언가를 하지만 변경에 대한 실행 취소를 하기를 원한다.  케어테이커는 먼저 오리지네이터에게 메멘토 객체를 요청한다. 그 뒤 예정된 일련의 명령을 수행한다. 명령 이전의 상태로 되돌리기 위해 메멘토 객체를 오리지네이터에 반환한다. 메멘토 객체 자신은 불투명 자료형(케어테이커는 변경할 수 없거나 변경해서는 안 되는)이다.

메멘토 패턴의 전형적인 예로 유사난수의 시드(seed), 유한 상태 기계의 상태(state)를 포함한다.

## 개요
메멘토 디자인 패턴은 유연하고 재사용 가능한 객체 지향 소프트웨어를 설계하기 위해 반복되는 디자인 문제를 해결하는 방법, 즉 객체는 구현, 변경, 테스트, 재사용이 쉬워야 한다는 것을 기술하는, 잘 알려진 23가지 GoF 디자인 패턴들 중 하나이다. 메멘토 패턴은  초기 HP 제품을 위해 Noah Thompson과 Dr.Drew Clinkenbeard에 의해 제작되었다.

메멘토 디자인 패턴이 해결할 수 있는 문제는 무엇인가?

* 객체의 내부 상태가 외부적으로 저장되어야 이후에 객체가 그 상태를 복구할 수 있다.
* 객체의 캡슐화가 훼손되지 않아야 한다.

문제는 잘 디자인된 객체는 캡슐화되어 있기 때문에 그 표현(데이터 구조)이 내부에 숨겨져 있어 객체의 외부에서 접근할 수 없다는 것이다.

메멘토 디자인 패턴이 설명하는 해결책은 무엇인가?

객체(오리지네이터) 자체가 다음 책임을 갖도록 한다.

* (메멘토) 객체에 그 내부 상태를 저장
* (메멘토) 객체로부터 이전 상태를 복구

메멘토를 생성한 오리지네이터만이 접근이 허용된다.

클라이언트(케어테이커)는 오리지네이터로부터 메멘토를 요청하고(오리지네이터의 내부 상태를 저장하기 위해) 오리지네이터에게 메멘토를 다시 돌려줄 수 있다(이전 상태로 복구하기 위해).

이는 캡슐화를 훼손하지 않고 오리지네이터의 내부 상태를 저장하고 복구할 수 있게해준다.

아래 UML 클래스 및 시퀀스 다이어그램을 함께 보자.

## 구조
### UML 클래스 및 시퀀스 다이어그램
frame|none|메멘토 디자인 패턴을 위한 샘플 UML 클래스와 시퀀스 다이어그램.위 UML 클래스 다이어그램에서, `Caretaker` 클래스는 오리지네이터의 내부 상태를 저장(`createMemento()`)하고 복구(`restore(memento)`)하기 위해 `Originator` 클래스를 참조한다.

`Originator` 클래스는 다음을 구현한다.

(1) 오리지네이터의 현재 내부 상태를 저장하는 `Memento` 객체를 생성하고 돌려주는 `createMemento()`

(2) `Memento` 객체로 전달된 상태를 복구하는 `restore(memento)`

UML 시퀀스 다이어그램은 런타임 인터렉션을 보여준다.

(1) 오리지네이터의 내부 상태를 저장: `Caretaker` 객체는, `Memento` 객체를 생성하고 현재 내부 상태를 저장하고(`setState()`), `Caretaker`로 `Memento`를 돌려주는 `Originator` 객체의 `createMemento()`를 호출한다.

(2) 오리지네이터의 내부 상태를 복구: `Caretaker`는 `Originator` 객체의 `restore(memento)`를 호출하고 복구할 상태를 저장하고있는 `Memento` 객체를 가져온다(`getState()`).

## 자바 예시
다음 자바 프로그램은 메멘토 패턴의 "undo" 사용을 보여준다.
 **import** **java.util.List**;
 **import** **java.util.ArrayList**;
 **class** **Originator** {
     **private** String state;
     *// The class could also contain additional data that is not part of the*
     *// state saved in the memento..*

     **public** void set(String state) {
         **this**.state = state;
         System.out.println("Originator: Setting state to " + state);
     }

     **public** Memento saveToMemento() {
         System.out.println("Originator: Saving to Memento.");
         **return** **new** Memento(**this**.state);
     }

     **public** void restoreFromMemento(Memento memento) {
         **this**.state = memento.getSavedState();
         System.out.println("Originator: State after restoring from Memento: " + state);
     }

     **public** **static** **class** **Memento** {
         **private** **final** String state;

         **public** Memento(String stateToSave) {
             state = stateToSave;
         }

         *// accessible by outer class only*
         **private** String getSavedState() {
             **return** state;
         }
     }
 }

 **class** **Caretaker** {
     **public** **static** void main(String[] args) {
         List savedStates = **new** ArrayList();

         Originator originator = **new** Originator();
         originator.set("State1");
         originator.set("State2");
         savedStates.add(originator.saveToMemento());
         originator.set("State3");
         *// We can request multiple mementos, and choose which one to roll back to.*
         savedStates.add(originator.saveToMemento());
         originator.set("State4");

         originator.restoreFromMemento(savedStates.get(1));
     }
 }
출력은 다음과 같다.
 Originator: Setting state to State1
 Originator: Setting state to State2
 Originator: Saving to Memento.
 Originator: Setting state to State3
 Originator: Saving to Memento.
 Originator: Setting state to State4
 Originator: State after restoring from Memento: State3
This example uses a String as the state, which is an immutable object in Java. In real-life scenarios the state will almost always be an object, in which case a copy of the state must be done.

이 예시에서는 Java에서 불변한 객체인 String을 상태로써 사용한다. 실제 시나리오에서는 상태가 거의 항상 객체일 것이며, 상태의 복사가 반드시 이루어져야 한다.

내부 클래스를 선언한것에 대해 구현이 취약점을 보인다고 말할 수 있을 것이다. 이 메멘토 전략은 오리지네이터가 하나 이상일 때 적용한다면 더 나을 것이다.

메멘토를 달성하는 다른 세 가지 주요 방법은 다음과 같다.

# 직렬화.
# 동일한 패키지에 선언된 클래스.
# 객체에 저장/복구 작업을 할 수 있는 프록시를 통해 접근할 수도 있는 객체.

## C# 예시
메멘토 패턴은 캡슐화를 훼손하지 않고 객체의 내부 상태를 캡쳐할 수 있게 해주므로 이후에 필요한 경우 변경사항을 되돌리거나 취소할 수 있다. 다음 코드에서는 객체에서 발생한 변경사항을 취소하기 위해 *메멘토 객체*가 실제로 사용되는 것을 볼 수 있다.
  1 **public** **class** **OriginalObject**
  2 {
  3     **public** string String1 { **get**; **set**; }
  4     **public** string String2 { **get**; **set**; }
  5
  6     **public** OriginalObject(string str1, string str2)
  7     {
  8         **this**.String1 = str1;
  9         **this**.String2 = str2;
 10     }
 11
 12     **public** **void** SetMemento(Memento memento)
 13     {
 14         **this**.String1 = memento.string1;
 15         **this**.String2 = memento.string2;
 16     }
 17
 18     **public** Memento CreateMemento()
 19     {
 20         **return** **new** Memento(**this**.String1, **this**.String2);
 21     }
 22 }
 23
 24 *//Memento object*
 25 **public** **class** **Memento**
 26 {
 27     **public** **readonly** string string1;
 28     **public** **readonly** string string2;
 29
 30     **public** Memento(string str1, string str2)
 31     {
 32         **this**.string1 = str1;
 33         **this**.string2 = str2;
 34     }
 35 }
 36
 37 *//CareTaker Object*
 38 **public** **class** **CareTaker**
 39 {
 40     **public** Memento Memento { **get**; **set**; }
 41 }
 42
 43 *//Client*
 44 **class** **Program**
 45 {
 46     **static** **void** Main(string[] args)
 47     {
 48         *// Create Originator object which container first state of "First" and "One".*
 49         *// The Memento will remember these value.*
 50         OriginalObject original = **new** OriginalObject("First", "One");
 51
 52         *// Create first State and store to caretaker*
 53         Memento firstMemento = original.CreateMemento();
 54         CareTaker caretaker = **new** CareTaker();
 55         caretaker.Memento = firstMemento;
 56
 57         *// Change into second state; "Second" and "Two".*
 58         original.String1 = "Second";
 59         original.String2 = "Two";
 60
 61         *// Retrieve back first State*
 62         original.SetMemento(caretaker.Memento);
 63
 64     }
 65 }

## Python 예시
 *"""*
 *Memento pattern example.*
 *"""*

 **class** **Memento**(object):
     **def** __init__(self, state):
         self._state = state

     **def** get_saved_state(self):
         **return** self._state

 **class** **Originator**(object):
     _state = ""

     **def** set(self, state):
         **print**("Originator: Setting state to", state)
         self._state = state

     **def** save_to_memento(self):
         **print**("Originator: Saving to Memento.")
         **return** Memento(self._state)

     **def** restore_from_memento(self, memento):
         self._state = memento.get_saved_state()
         **print**("Originator: State after restoring from Memento:", self._state)

 saved_states = []
 originator = Originator()
 originator.set("State1")
 originator.set("State2")
 saved_states.append(originator.save_to_memento())

 originator.set("State3")
 saved_states.append(originator.save_to_memento())

 originator.set("State4")

 originator.restore_from_memento(saved_states[0])

## 각주

## 외부 링크
* Description of Memento Pattern in Ada
* Memento UML Class Diagram with C# and .NET code samples
* SourceMaking Tutorial
* 

분류:소프트웨어 디자인 패턴