[Design Pattern] 메멘토 패턴

Memento Pattern

메멘토 패턴이란 객체의 상태를 이전 상태로 회귀할 수 있는 기능을 제공하는 디자인 패턴이다.

활용성

  1. 어떤 객체의 상태에 대한 스냅샷을 저장한 후 나중에 이 상태로 복구해야 할 때
  2. 상태를 얻는데 필요한 직접적인 인터페이스를 두면 그 객체의 구현 세부사항이 드러날 수밖에 없고 이것으로 객체의 캡슐화가 깨질 때

메멘토 패턴 사용에 따른 결과

캡슐화된 경계 유지

원조본만 메멘토를 다룰 수 있기 때문에 메멘토가 외부에 노출되지 않는다. 메멘토 패턴은 Originator 클래스의 내부 상태를 다른 객체로 분리하는 방법으로 상태에 대한 정보의 캡슐화를 보장한다.

Originator 클래스의 단순화

다른 방법으로 캡슐화를 유지하며 이전 상태로의 회귀를 가능하게 하려면 Originator가 다양한 버전의 내부 상태를 모두 저장해야하는 문제가 있다. 사용자가 자신들이 필요한 상태를 별도로 관리하게 되면 Originator 클래스는 단순해진다.

비용 증가

Originator 클래스가 많은 양의 정보를 저장해야 하거나 상당히 자주 메멘토를 반환해야 할 때라면 메멘토가 상당한 오버헤드를 가져오게 된다.

메멘토 관리의 책임

보관자 객체는 보관하는 메멘토를 삭제할 책임이 있다. 그러나 보관자 쪽에서는 얼마나 많은 상태가 메멘토에 저장되어 있는지 모르기 때문에 보관자 객체가 가볍다 해도 메멘토를 저장할 때 적지 않은 저장 비용을 유발한다.

메멘토

구조

메멘토의 구조는 다음과 같다.

Memento Pattern Diagram

C++ 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void MementoPattern::MementoPattern()
{
Caretaker* caretaker = new Caretaker();
Originator* originator = new Originator(State{.col = 3, .row = 'A'}, "Pawn");

caretaker->save(originator->createMemento());
originator->move(State{.col = 4, .row = 'A'});

caretaker->save(originator->createMemento());
originator->move(State{.col = 5, .row = 'A'});

caretaker->save(originator->createMemento());
originator->move(State{.col = 5, .row = 'B'});

originator->setMemento(caretaker->undo());
originator->setMemento(caretaker->undo());
originator->setMemento(caretaker->undo());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Caretaker
{
std::list<Memento*> mementos;
public:
void save(Memento* memento)
{
mementos.push_back(memento);
}

Memento* undo()
{
Memento* memento = mementos.back();
mementos.pop_back();
return memento;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Memento
{
State state;
public:
Memento(State state) : state(state) {}

void setState(State state)
{
this->state = state;
}

State getState()
{
return state;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Originator
{
State state;
std::string name;

public:
Originator(State state, std::string name) : state(state), name(name) {}

Memento* createMemento()
{
return new Memento(this->state);
}

void setMemento(Memento* memento)
{
this->state = memento->getState();
delete memento;
std::cout<<name<<" Undo col : "<<state.col<<" row : "<<state.row<<std::endl;
}

void move(State state)
{
this->state = state;
std::cout<<name<<" Move col : "<<state.col<<" row : "<<state.row<<std::endl;
}
};
1
2
3
4
5
struct State
{
int col;
char row;
};

java 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Client {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Originator originator = new Originator(new State(3, 'A'), "Pawn");

caretaker.save(originator.createMemento());
originator.move(new State(4, 'A'));

caretaker.save(originator.createMemento());
originator.move(new State(5, 'A'));

caretaker.save(originator.createMemento());
originator.move(new State(5, 'B'));

originator.setMemento(caretaker.undo());
originator.setMemento(caretaker.undo());
originator.setMemento(caretaker.undo());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Caretaker {
private List<Memento> mementoList;

public Caretaker()
{
mementoList = new ArrayList<>();
}

void save(Memento memento) {
mementoList.add(memento);
}

Memento undo(){
return mementoList.remove(mementoList.size() - 1);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Memento {
private State state;

public Memento(State state) {
this.state = state;
}

public void setState(State state) {
this.state = state;
}

public State getState() {
return this.state;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Originator {
private State state;
private String name;

public Originator(State state, String name) {
this.state = state;
this.name = name;
}

public Memento createMemento() {
return new Memento(this.state);
}

public void setMemento(Memento memento) {
this.state = memento.getState();
System.out.println(name + " Undo col : " + this.state.getCol() + " row : " + this.state.getRow());
}

public void move(State state) {
this.state = state;
System.out.println(name + " Move col : " + this.state.getCol() + " row : " + this.state.getRow());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class State {
private int col;
private char row;

public State(int col, char row) {
this.col = col;
this.row = row;
}

public char getRow() {
return row;
}

public int getCol() {
return col;
}
}
Author: Song Hayoung
Link: https://songhayoung.github.io/2020/08/26/Design%20Pattern/MementoPattern/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.