ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 메멘토 패턴 (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

     
Designed by Tistory.