메멘토(Memento) 패턴
메멘토 패턴은 객체의 상태를 저장하고 복원할 수 있게 해주는 행동 디자인 패턴입니다. 이 패턴은 객체의 상태를 캡슐화하여 외부에서 해당 상태를 직접 접근하지 않고도 이전 상태로 되돌릴 수 있는 기능(Undo)을 제공합니다.
문제점: 객체의 상태를 안전하게 저장하고 복원하기
애플리케이션에서 되돌리기(Undo) 기능이 필요할 때, 객체의 상태를 저장해야 합니다. 하지만 객체의 상태를 외부에서 직접 조작하면 캡슐화 원칙이 깨지고 유지보수가 어려워질 수 있습니다.
class Editor {
private String content;
public void setContent(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
public class Main {
public static void main(String[] args) {
Editor editor = new Editor();
// 상태 설정
editor.setContent("Version 1");
System.out.println(editor.getContent()); // Version 1
// 상태 변경
editor.setContent("Version 2");
System.out.println(editor.getContent()); // Version 2
// 상태를 이전으로 되돌리고 싶으면 직접 값을 설정해야 함
editor.setContent("Version 1");
System.out.println(editor.getContent()); // Version 1
}
}
문제점
- 캡슐화가 깨짐: 객체의 상태를 직접 조작해야 합니다.
- 복잡한 상태 관리: 여러 상태를 관리하거나 되돌리기 기능을 구현하려면 코드가 복잡해질 수 있습니다.
- 유연성 부족: 더 많은 상태를 추가하거나 여러 단계로 되돌리는 기능을 쉽게 구현하기 어렵습니다.
해결 방법: 메멘토 패턴 적용
메멘토 패턴은 객체의 상태를 메멘토 객체로 저장하여 안전하게 복원할 수 있도록 합니다. 이 패턴은 Originator(원본 객체), Memento(메멘토 객체), 그리고 Caretaker(관리자 객체) 세 가지로 구성됩니다.
Orignator(원본 객체) 클래스
class Editor {
private String content;
public void setContent(String content) {
this.content = content;
}
public String getContent() {
return content;
}
// 현재 상태를 메멘토 객체로 저장
public Memento save() {
return new Memento(content);
}
// 메멘토 객체에서 상태 복원
public void restore(Memento memento) {
this.content = memento.getContent();
}
}
Memento 클래스
class Memento {
private final String content; // 상태를 불변으로 유지
public Memento(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
Caretaker(관리자) 클래스
class History {
private Stack<Memento> history = new Stack<>();
public void save(Editor editor) {
history.push(editor.save());
}
public void undo(Editor editor) {
if (!history.isEmpty()) {
editor.restore(history.pop());
}
}
}
클라이언트 코드
public class Main {
public static void main(String[] args) {
Editor editor = new Editor();
History history = new History();
// 상태 설정 및 저장
editor.setContent("Version 1");
history.save(editor);
System.out.println(editor.getContent()); // Version 1
editor.setContent("Version 2");
history.save(editor);
System.out.println(editor.getContent()); // Version 2
// 상태 되돌리기
history.undo(editor);
System.out.println(editor.getContent()); // Version 1
history.undo(editor);
System.out.println(editor.getContent()); // (빈 문자열)
}
}
메멘토 패턴의 동작 원리
- Originator(원본 객체): 자신의 상태를 메멘토 객체에 저장하고 복원합니다.
- Memento(메멘토 객체): Originator의 상태를 캡슐화합니다.
- Caretaker(관리자 객체): 메멘토 객체를 저장하고 필요할 때 복원합니다.
메멘토 패턴의 장단점
장점
- 캡슐화 유지: 객체의 내부 상태를 외부에 노출하지 않고 저장하고 복원할 수 있습니다.
- Undo/Redo 기능 구현 용이: 여러 단계의 상태를 관리할 수 있습니다.
- 유연성 향상: 객체의 상태가 변경되더라도 클라이언트 코드에 영향을 미치지 않습니다.
단점
- 메모리 사용 증가: 모든 상태를 메멘토 객체로 저장하면 메모리 사용량이 늘어날 수 있습니다.
- 복잡성 증가: 객체의 상태가 복잡할수록 메멘토 패턴을 구현하는 코드도 복잡해집니다.
- Caretaker 관리 부담: 여러 메멘토를 관리해야 하는 Caretaker의 역할이 복잡해질 수 있습니다.
결론
메멘토 패턴은 객체의 상태를 안전하게 저장하고 복원하는 데 유용한 패턴입니다. 이 패턴은 Undo/Redo 기능과 같이 여러 단계로 상태를 되돌릴 수 있는 기능이 필요한 경우에 적합합니다.
하지만 메모리 사용량이 늘어날 수 있으므로, 메멘토 객체의 사용을 신중히 관리해야 합니다. 텍스트 편집기, 게임 상태 저장, 트랜잭션 관리와 같은 애플리케이션에서 자주 사용됩니다.