메멘토 패턴
캡슐화를 유지하면서 객체 내부 상태를 외부에 저장하는 방법
- 객체 상태를 외부에 저장했다가 해당 상태로 다시 복귀할 수 있다.
- 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 |