-
메멘토 패턴 (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()); } } }
참고
'디자인 패턴' 카테고리의 다른 글
전략 패턴 (Strategy Pattern) (1) 2022.08.13 상태 패턴 (State Pattern) (0) 2022.08.13 중재자 패턴 (Mediator pattern) (0) 2022.08.11 이터레이터 패턴 (Iterator Pattern) (0) 2022.08.04 인터프리터 패턴 (Interpreter Pattern) (0) 2022.08.03