Pattern - Memento

2 minute read

모든 일은 다 내가 정한 것을. 결국 내가 감내해야할 부분이겠지.

의도


캡슐화를 위배하지 않은 채 어떤 객체의 내부 상태를 잡아내고 실체화 시켜 둠으로써, 이후 해당 객체가 그 상태로 되돌아올 수 있도록 합니다.

동기


때에 따라서는 객체의 내부 상태를 기록해 둘 필요가 있습니다. 예를 들어, 체크 포인트를 구현할 때나 오류를 복구하거나 연산 수행 결과를 취소하는 매커니즘을 구현하려면 내부 상태 기록이 절대적으로 필요합니다. 객체를 이전 상태로 복구하려면 복구할 시점의 상태 정보가 있어야 합니다. 그러나 객체는 자체적으로 상태의 일부나 전부를 캡슐화 하여 상태를 외부에 공개하지 않기 때문에, 다른 객체는 상태에 접근하지 못합니다.
예) 객체 사이의 연결을 가능하게 하는 그래픽 편집기

활용성


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

항목에 대한 설명


  • Memento
    SolverState
    원조본 객체의 내부 상태를 저장합니다. 메멘토는 원조본 객체의 내부 상태를 필요한 만큼 저장해둡니다. 메멘토는 원조본 객체를 제외한 다른 객체는 자신에게 접근할 수 없도록 막습니다. 그래서 Memento 클래스에는 사실상 두 종류의 인터페이스가 있습니다. 관리 책임을 갖는 CareTaker 클래스는 Memento에 대한 제한 범위 인터페이스만을 볼 수 있습니다. 즉, CareTaker는 메멘토를 다른 객체에게 넘겨 줄 수 만 있습니다. 이에 반해, Originator 클래스에게는 광범위 인터페이스가 보입니다. 즉, 자신의 상태를 이전 상태로 복원하기 위해 필요한 모든 자료에 접근하게 해 주는 인터페이스입니다. 이상적으로는 메멘토를 생성하는 원조본 객체만이 메멘토의 내부 상태에 접근할 수 있는 권한을 갖습니다.

  • Originator
    ConstraintSolver
    원조본 객체입니다. 메멘토를 생성하여 현재 객체의 상태를 저장하고 메멘토를 사용하여 내부 상태를 복원합니다.

  • CareTaker
    실행 취소 메커니즘
    메멘토의 보관을 책임지는 보관자입니다. 메멘토 내용을 검사하거나 그 내용을 건드리지는 않습니다.

협력방법


  • 보관자 객체는 원조본 객체에 메멘토 객체를 요청합니다. 또 요청한 시간을 저장하며, 받은 메멘토 객체를 다시 원조본 객체에게 돌려줍니다. 보관자 객체는 메멘토 객체를 원조본 객체에 전달하지 않을 수도 있습니다. 원조본 객체가 이전 상태가 돌아갈 필요가 없을 대는 전달할 필요가 없기 때문입니다.
  • 메멘토 객체는 수동적입니다. 메멘토 객체를 생성한 원조본 객체만이 상태를 설정하고 읽어올 수 있습니다.

결과


  • 캡슐화된 경계를 유지할 수 있습니다.
  • Originator 클래스를 단순화할 수 있습니다.
  • 메멘토의 사용으로 더 많은 비용이 들어갈 수도 있습니다.
  • 제한 범위 인터페이스와 광범위 인터페이스를 정의해야 합니다.
  • 메멘토를 관리하는 데 필요한 비용이 숨어있습니다.

명령 패턴을 실행 취소가 가능한 연산의 상태가 저장할 때 메멘토 패턴을 사용할 수 있습니다. 메멘토 패턴은 반복자 패턴에서의 반복 과정 상태를 관리할 수 있습니다.

코드 예제 – 기본 샘플



package designpattern.gof_memento.sample02;

public class GamePlay {

    public static void main(String[] args) {
        Game game = new Game();
        SaveFile storage = new SaveFile();

        storage.saveTempStorage("임시저장 1");
        
        storage.saveTempStorage("임시저장 2");

        game.addSaveList(storage.getGameSave());

        storage.saveTempStorage("임시저장 3");

        storage.saveTempStorage("임시저장 4");

        game.addSaveList(storage.getGameSave());

        storage.saveTempStorage("임시저장 5");
        storage.saveTempStorage("임시저장 6");

        System.out.println(game.loadSaveData(0).getStorage());
        System.out.println(game.loadSaveData(1).getStorage());


    }
}


package designpattern.gof_memento.sample02;

import java.util.ArrayList;
import java.util.List;

public class Game {

    private static List<Storage> listSave = new ArrayList<>();

    public void addSaveList(Storage storage){
        listSave.add(storage);
    }

    public Storage loadSaveData(int savePoint){
        return listSave.get(savePoint);
    }
}

package designpattern.gof_memento.sample02;

public class SaveFile {

    public String storage;

    public Storage getGameSave(){
        return new Storage(storage);
    }

    public void saveStorage(String gameStorage){
        this.storage = gameStorage;
    }

    public void saveTempStorage(String tempStorage){
        this.storage = tempStorage;
    }
}

package designpattern.gof_memento.sample02;

public class Storage {

    private String storage;

    public Storage(String storage){
        this.storage = storage;
    }

    public String getStorage(){
        return storage;
    }
}