Memento Pattern

3 분 소요

undo

Memento Pattern === "undo"

1. 메멘토 패턴(memento Pattern)이란?.

위키백과에 나와있는 메멘토 패턴의 정의는
‘객체를 이전 상태로 되돌릴 수 있는 기능을 제공하는 소프트웨어 디자인 패턴’이다.

쉽게 말해, 메멘토 패턴은 ‘undo’ 역할을 수행한다.
여기서 ‘undo’란 실행취소 혹은 수행한 기능을 되돌리는 것을 의미한다.


2. 메멘토 패턴의 구성과 역할

1) 구성

메멘토 패턴은 3개의 객체로 구현된다.

  • 오리지네이터(originator)
  • 메멘토(memento)
  • 케어테이커(caretaker)

2) 역할

각 객체의 역할들은 다음과 같다.

  • 오리지네이터(originator) : Memento 객체를 생성해서, Originator의 snapshot(그 때 기억)을 저장한다.
  • 메멘토(memento) : Originator의 state를 저장한다. Memento의 state는 외부에 노출시키지 않으며(primary), Originator에 의해서만 읽고 쓸 수 있다. 즉, Memento는 Originator의 inner class로 구현될 것이다.
  • 케어테이커(caretaker) : Memento를 저장할 책임이 있다.

3) Cycle

- UML

mementoUML

- 수행 과정

[상황]
caretaker는 originator에 대해 어떠한 실행을 한 후 변경에 대한 실행 취소를 하기를 원한다.

[수행]
① main에서 originator의 상태를 변경한다.
② caretaker는 originator에게 memento 특정 상태를 저장(saveToMemento)한다.
③ 사용자는 1,2번 과정을 반복하다가 원하는 savepoint로 되돌리기를 원한다.
④ memento 객체를 originator에게 반환(restoreFromMemento)하여 반환한다.


3. code

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<Originator.Memento> savedStates = new ArrayList<Originator.Memento>();
 
        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(0));
    }
}

▶︎ 출력

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: State2

4. 문제

  • 위 코드는 하나의 클래스로 구현한 memento 패턴이다.
  • 위 코드를 참조하여 아래 네 개의 클래스로 구성된 코드를 구현해보아라.
  • String형 데이터 하나와 Int형 데이터 하나에 대한 정보로 가지는 Information 객체를 구현.
● User : 메멘토 패턴이 적용 된 Information 객체를 실제로 사용하는 사용자.
● Information : 상태를 저장하고 복원 할 데이터를 가지고 있는 클래스.
● Memento : 특정 시점의 Information의 상태정보를 저장하는 클래스.
● CareTaker : 상태 정보가 저장되어 있는 Memento들을 관리하는 클래스. 내부에 Stack 자료형 변수를 가짐으로써 Memento 객체를 저장하고 복원함.

1) 힌트

UML

2) 해답

Information.java

public class Information {
    private String Data1;                            //Information이 가지고 있는 데이터1
    private int Data2;                                //Information이 가지고 있는 데이터2
    public Information(String Data1, int Data2)        //생성자입니다.
    {
        this.Data1 = Data1;
        this.Data2 = Data2;
    }
    public Memento CreateMemento()                    //Memento를 생성합니다 (상태저장) 
    {
        return new Memento(this.Data1,this.Data2);
    }
    public void RestorMemento(Memento memento)         //Memento를 복원합니다 (상태복원)
    {
        this.Data1 = memento.getData1();
        this.Data2 = memento.getData2(); 
    }
    public void set_Data1(String Data1)                //데이터1의 값을 지정
    {
        this.Data1 = Data1;
    }
    public void set_Data2(int Data2)                //데이터2의 값을 지정
    {
        this.Data2 = Data2;
    }
    public String get_Data1()                        //데이터 1의 값 반환
    {
        return this.Data1;
    }
    public int get_Data2()                            //데이터 2의 값 반환
    {
        return this.Data2;
    }
}


Memento.java

public class Memento {
    
    //Information의 상태정보를 가지고 있음
    private String Data1;
    private int Data2;
    
    public Memento(String Data1,int Data2)
    {
        this.Data1 = Data1;
        this.Data2 = Data2;
    }
    public String getData1()
    {
        return this.Data1;
    }
    public int getData2()
    {
        return this.Data2;
    }
}


CareTaker.java

import java.util.Stack;
 
public class CareTaker {
    
    Stack<Memento> mementos = new Stack<>();    //Memento 관리를 위한 Stack
    
    public void push(Memento memento)            //특정 시점에 생성된 Memento를 Push
    {
        mementos.push(memento);
    }
    public Memento pop()                        //복원을 위한 Memento 객체 반환
    {
            return mementos.pop();
    }
}


User.java

public class User {
    
    Information info;
    CareTaker caretaker;
    public void exe()
    {
        info  = new Information("Data1",10);            //Information 객체 생성
        caretaker = new CareTaker();                    //CareTaker 객체 생성
        //현재 Information의 상태 정보를 가지는 Memento를 생성하여 CareTaker에 추가합니다.
        caretaker.push(info.CreateMemento());
        
        //Information 정보를 수정합니다.                                                
        info.set_Data1("Data2");
        info.set_Data2(20);
        //현재 Information의 상태정보를 출력합니다.
        System.out.println("현재 Data1 : " + info.get_Data1());
        System.out.println("현재 Data2 : " + info.get_Data2());
        //가장 최근에 생성 된 Memento를 가지고와서 상태 정보를 복원합니다.
        info.RestorMemento(caretaker.pop());
        //상태 정보를 복원 한 후에 Information의 상태 정보를 출력합니다.
        System.out.println("복구된 Data1 : " + info.get_Data1());
        System.out.println("복구된 Data2 : " + info.get_Data2());
    }
}

▶︎ 출력

현재 Data1 : Data2
현재 Data2 : 20
복구된 Data1 : Data1
복구된 Data2 : 10

# 참조 사이트

댓글남기기