디자인 패턴

메멘토 패턴 (Memento Pattern)

개발정리 2022. 8. 12. 00:21

메멘토 패턴


캡슐화를 유지하면서 객체 내부 상태를 외부에 저장하는 방법

  • 객체 상태를 외부에 저장했다가 해당 상태로 다시 복귀할 수 있다.
  • Originator - 우리가 저장할 객체이다. Originator 의 state 를 저장할 것이다.
  • CareTaker - Originator 의 내부 정보를 CareTaker 가 가져와서 저장한다.
  • Memento - 내부정보를 추상화한 클래스이다. CareTaker 는 Originator 의 디테일한 정보를 직접적으로 가지는 것이 아니라Memento  타입으로 가진다. Memento 는 Immutable 한 객체여야 한다.

 

 

 

메멘토 패턴 적용 전


게임 중에 게임을 중지시켰다가 다시 실행해도 중지 전까지 스코어는 유지되어야 한다.

간단하게 코드로 보면 아래와 같이 구현해 볼 수 있다.

 

 

Game

@Getter
@Setter
public class Game {
  private int redTeamSscore;
  private int blueTeamSscore;
}

 

 

Client

public class Client {
  public static void main(String[] args) {
    Game game = new Game();
    game.setRedTeamSscore(10);
    game.setBlueTeamSscore(20);

    int redTeamSscore = game.getRedTeamSscore();
    int blueTeamSscore = game.getBlueTeamSscore();

    Game restoreGame = new Game();
    restoreGame.setRedTeamSscore(redTeamSscore);
    restoreGame.setBlueTeamSscore(blueTeamSscore);
  }
}

red 팀 score 는 10, blue 팀 score 는 20 이다. 

이러한 정보를 각각 redTeamScore, blueTeamScore 변수에 저장해두고, 새로 생성된 Game 객체 (restoreGame) 에 score 정보를 저장한다.

 

 

문제점

Client 가 매번 Game score  에 대해 알고 있어야한다.

이를 메멘토 패턴을 적용하여 해결해보자.

 

 

 

메멘토 패턴 적용 후


Memento 인 GameSave 를 보자

// Memento
@Getter
public class GameSave {
  // final 를 통해 불변하도록 설정, setter 를 만들지 않는다.
  private final int redTeamScore;
  private final int blueTeamScore;

  public GameSave(int redTeamScore, int blueTeamScore) {
    this.redTeamScore = redTeamScore;
    this.blueTeamScore = blueTeamScore;
  }
}

Memento 는 GameSave 가 가지고 있는 필드를 모두 가지고 있고, 불변 객체이다.

즉, score 정보들은 한번 생성자를 통해 한번 생성되면 절대 변하지 않는다.

 

 

Originator 인 Game 은 이전과 비슷하며 아래와 같은 두 기능이 추가된다.

  • Game 의 상태를 GameSave 에 저장하는 save 메소드
  • GameSave 로 부터 이전의 상태를 되돌리는 restore 메소드
@Getter
@Setter
// Originator
public class Game {
  private int redTeamScore;
  private int blueTeamScore;
  
  // 현재 상태 저장
  public GameSave save() {
    return new GameSave(this.redTeamScore, this.blueTeamScore);
  }
  
  // 이전 상태 복구
  public void restore(GameSave gameSave) {
    this.redTeamScore = gameSave.getRedTeamScore();
    this.blueTeamScore = gameSave.getBlueTeamScore();
  }
}

 

 

Client (CareTaker) 는 이제 직접적으로 Game 의 socre 에 대한 정보를 알 필요 없이 GameSave(Memento) 에 요청한다.

public class Client {
  public static void main(String[] args) {

    Game game = new Game();

    // 게임 초반 상태
    game.setRedTeamScore(10);
    game.setRedTeamScore(20);

    // 게임의 state 정보를 GameSave 에 저장 (Client 는 CareTaker)
    GameSave memento = game.save();
    
    // game 의 정보 수정
    game.setRedTeamScore(12);
    game.setBlueTeamScore(22);
    
    // game 을 memento 에 저장된 정보로 restore
    game.restore(memento);
    System.out.println(game.getRedTeamScore());
    System.out.println(game.getBlueTeamScore());
  }
}

 

 

 

장점과 단점


장점

  • 캡슐화를 지키면서 상태 객체의 스냅샷을 만들 수 있다.
  • 객체 상태를 저장하고 복원하는 역할을 CareTaker 에게 위임할 수 있다.
  • 객체 상태가 바뀌어도 클라이언트 코드는 바뀌지 않는다.

 

단점

  • 많은 정보를 저장하는 Memento 를 자주 생성하는 경우 메모리 사용량에 많은 영향을 줄 수 있다.

 

 

 

실무에선 어떻게 쓰이나?


자바 직렬화를 제공한다.

직렬화를 통해 Originate 의 상태를 파일에 저장한 후 파일에 저장된 state 정보를 역직렬화하여 restore 할 수 있다.

public class MementoInJava {

  public static void main(String[] args) throws IOException, ClassNotFoundException {
    // Serializable
    Game game = new Game();
    game.setRedTeamScore(10);
    game.setBlueTeamScore(20);
    
    // 직렬화
    // 파일에 score 상태를 저장
    try(FileOutputStream fileOutputStream = new FileOutputStream("GameSave.hex");
    ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream))
    {
      outputStream.writeObject(game);
    }
    
    game.setBlueTeamScore(25);
    game.setRedTeamScore(15);
    
    // 역직렬화
    // 파일에 scroe 상태를 불러와서 restore 하기
    try(FileInputStream fileInputStream = new FileInputStream("GameSave.hex");
    ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream))
    {
      game = (Game)objectInputStream.readObject();
      System.out.println(game.getBlueTeamScore());
      System.out.println(game.getRedTeamScore());
    }
  }

}

 

 

 

참고


 

코딩으로 학습하는 GoF의 디자인 패턴 - 인프런 | 강의

디자인 패턴을 알고 있다면 스프링 뿐 아니라 여러 다양한 기술 및 프로그래밍 언어도 보다 쉽게 학습할 수 있습니다. 또한, 보다 유연하고 재사용성이 뛰어난 객체 지향 소프트웨어를 개발할

www.inflearn.com