2022. 2. 17. 17:47ㆍETC/Design Patterns
Object Behavioral Pattern
Memento Pattern
----------------- INDEX -----------------
Memento Pattern ?
Structure
Sample Code: Java
Nested Class
관련 패턴
----------------------------------------------
Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.
- GoF Design Patterns
메멘토 패턴은 객체의 상태를 저장하여 이전 상태로 복구하는 패턴입니다.
memento의 사전적 의미를 찾아보면 ‘사람·장소를 기억하기 위한 기념품'입니다.
혹은, 기억의 증표라고도 합니다.
이런 의미와 유사하게 메멘토 패턴은 객체 상태를 다른 객체에 저장했다가 다시 복원합니다.
객체를 복원할 때 캡슐화에 영향을 주지 않으면서도 안전하게 복원하는 절충안이 필요합니다.
메멘토 패턴은 캡슐화를 위반하지 않고 개체의 내부 상태를 캡처하고 외부화하여 나중에 개체를 이 상태로 복원할 수 있습니다.
저장과 복원 작업을 처리하는 중간 매개체를 이용하여 보다 쉽게 상태 이력을 관리할 수 있습니다.
Encapsulation
객체가 갖는 속성 값들은 한 번 바뀌면 다시 이전 상태로 되돌릴 수 없습니다.
객체의 상태를 되돌리기 위해 기록을 해둘 수도 있고, 이전의 행동을 반대로 실행할 수도 있습니다.
하지만 단순히 하나의 객체만 복원하는 것이 아니라,
복원 객체와 의존 관계인 모든 객체를 함께 이전 상태로 복원해야 하는 것은 굉장히 힘든 작업입니다.
연관 객체들과 같이 복원하기 위해서는 다른 객체의 접근이 필요하고 객체의 캡슐화를 망가뜨릴 수도 있습니다.
그래서 객체를 복원하기 위한 특별한 관리 방법이 필요한데,
메멘토 패턴은 캡슐화를 파괴하지 않고 객체 상태를 저장하는 방법을 제안합니다.
Memento 객체는 상태를 저장하고자 하는 다른 객체의 하나의 스냅샷을 저장합니다.
undo 기능은 Originator로 부터 메멘토를 요청하는 메커니즘을 가집니다. 오직 Originator만 메멘토로부터 정보를 저장하고 검색할 수 있습니다. 이 메멘토는 다른 객체들에 불투명합니다.
Structure
Memento
- Originator 객체의 내부 상태를 저장합니다.
: Memento 객체는 Originator의 재량에 따라 필요한 시점의 Originator의 내부 상태를 저장할 수 있습니다.
- Originator가 아닌 다른 개체의 접근을 막습니다.
Originator
- Memento를 사용하여 해당 객체의 내부 상태를 복원합니다.
- 현재 내부의 상태 스냅샷을 포함하는 Memento를 만듭니다.
- Memento를 제작한 Originator만이 Memento의 내부 상태에 접근할 수 있습니다.
Caretaker
- Memento을 보관하는 책임을 가집니다.
- 절대 memento의 내용을 검사examines하거나 조작하지 않습니다.
Memento & Interface
메멘토는 CareTaker와 Memento, 두 개의 인터페이스로 객체에 대한 접근을 관리합니다.
Gof는 CareTaker는 좁은narrow 인터페이스로, Originator를 넓은wide 인터페이스로 본다고 표현을 합니다.
이는 각각의 인터페이스가 메멘토를 접근할 때의 범위라고 생각하면 됩니다.
CareTaker는 메멘토를 다른 객체로 전달하는 역할을 할 수 있습니다.
위에서 소개한대로, 검사나 조작을 하지 않고 Memento 객체 내부에 접근하지 않기 때문에 좁은 인터페이스라고 표현합니다.
반면 Originator는 이전 상태로 복원하는 데 필요한 모든 데이터에 액세스할 수 있습니다.
따라서 Memento에 광범위한 접근을 한다고 표현할 수 있죠.
Sample Code: Java
Editor를 사용하고 있고, 이 Editor는 Text와 그 Text의 위치 posX, posY를 갖고 있습니다.
즉, 관리하고자 하는 state가 Text와 posX, posY가 됩니다.
이 Editor에 복구 기능을 추가한다고 할 때, Memento에 해당하는 Snapshot을 생성하고
CareTaker로 Command 객체를 지정해보도록 할게요.
✔️ Originator - Editor
public class Editor {
protected String text;
protected int posX, posY;
public Snapshot create() {
System.out.println("Create Snapshot");
return new Snapshot(this, text, posX, posY);
}
public void restore(Snapshot snapshot) {
System.out.println("Restore to Snapshot");
this.text = snapshot.getText();
this.posX = snapshot.getPosX();
this.posY = snapshot.getPosY();
}
public String getstate() {
return "Text (" + posX + ", "+ posY + ") : " + this.text;
}
public void setText(String text) {
this.text = text;
}
public void setPos(int posX, int posY) {
this.posX = posX;
this.posY = posY;
}
}
✔️ Memento
public class Snapshot {
private Editor editor;
private String text;
private int posX, posY;
public Snapshot(Editor editor, String text, int posX, int posY) {
this.editor = editor;
this.text = text;
this.posX = posX;
this.posY = posY;
}
public String getText() {
return text;
}
public int getPosX() {
return posX;
}
public int getPosY() {
return posY;
}
}
✔️ CareTaker
public class Command {
protected Stack<Snapshot> stack;
public Command() {
this.stack = new Stack<>();
}
public void makeBackup(Editor origin) {
Snapshot memento = origin.create();
stack.push(memento);
}
public Editor undo(Editor editor) {
Snapshot memento = stack.pop();
editor.restore(memento);
return editor;
}
}
✔️ Client
public class Client {
public static void main(String[] args) {
Editor editor = new Editor();
Command care = new Command();
editor.setText("design pattern 1 - Memento");
editor.setPos(10, 24);
System.out.println(editor.getstate());
care.makeBackup(editor);
editor.setText("design pattern 2 - Iterator");
editor.setPos(22, 24);
System.out.println(editor.getstate());
care.makeBackup(editor);
editor.setText("design pattern 3 - Prototype");
editor.setPos(44, 24);
System.out.println(editor.getstate());
care.makeBackup(editor);
System.out.println("\n=== 복구 ===\n");
Editor obj = care.undo(editor);
System.out.println(obj.getstate());
obj = care.undo(editor);
System.out.println(obj.getstate());
obj = care.undo(editor);
System.out.println(obj.getstate());
}
}
3개의 텍스트를 삽입한 후, 스냅샷을 생성해 기록했습니다.
그 후 다시 복구하는 기능을 제작했어요.
출력값이 예상가시나요?
Output
Text (10, 24) : design pattern 1 - Memento
Create Snapshot
Text (22, 24) : design pattern 2 - Iterator
Create Snapshot
Text (44, 24) : design pattern 3 - Prototype
Create Snapshot
=== 복구 ===
Restore to Snapshot
Text (44, 24) : design pattern 3 - Prototype
Restore to Snapshot
Text (22, 24) : design pattern 2 - Iterator
Restore to Snapshot
Text (10, 24) : design pattern 1 - Memento
Nested Class
메멘토를 Originator 내부에 중첩 클래스로 정의할 수도 있습니다.
아주 단순한 구조라면, 중첩 클래스를 사용할 수도 있습니다.
코드는 따로 Github 에 올려두었으니, 확인할 수 있습니다.
관련 패턴
C- Creational Patterns | S - Structural Patterns | B - Behavioral Patterns
메멘토는 객체 상태를 저장하기 위해 현재 시점의 객체를 복제합니다.
새로운 객체를 생성하고 현재 상태를 재설정하는 과정은 복잡합니다.
이 경우 Prototype 패턴을 이용하여 객체를 복사할 수 있습니다.
객체를 복제하면 현재 시점의 상태를 가진 객체를 빠르게 생성할 수 있습니다.
명령 패턴은 커멘드를 캡슐화하여 요청과 동작을 구분합니다.
이때 실행을 처리하는 메서드와 실행을 취소하는 undo 기능을 같이 만들어둘 수 있으며,
Undo 기능 구현 시 커맨드 패턴을 응용할 수 있습니다.
메멘토는 객체 상태값을 저장하며 상태값을 처리하는 관점에서 상태 패턴을 같이 응용할 수 있습니다.
케어테이커는 다수의 메멘토를 관리합니다.
다수의 메멘토는 배열과 스택 구조로 처리하는데, 이때 반복자 패턴을 통해 복원 과정을 반복 관리할 수 있습니다.
그럼 지금까지 Memento Pattern에 대해 알아보았습니다.
오타나 잘못된 내용이 있다면 댓글로 남겨주세요!
감사합니다 ☺️
'ETC > Design Patterns' 카테고리의 다른 글
Design Pattern, Flyweight (0) | 2022.02.18 |
---|---|
Design Pattern, Prototype (0) | 2022.02.17 |
Design Pattern, State (0) | 2022.02.16 |
Design Pattern, Visitor (0) | 2022.02.13 |
Design Pattern, Iterator (0) | 2022.02.10 |
Backend Software Engineer
𝐒𝐮𝐧 · 𝙂𝙮𝙚𝙤𝙣𝙜𝙨𝙪𝙣 𝙋𝙖𝙧𝙠