Design Pattern

[디자인 패턴] 메멘토 패턴 (Memento Pattern)

꾸준함. 2024. 6. 30. 16:01

메멘토 패턴

  • 캡슐화를 유지하면서 객체의 내부 상태를 외부에서 저장하고 복원할 수 있게 지원하는 디자인 패턴
  • 객체 상태를 외부에 저장했다가 해당 상태로 다시 복구할 수 있음
  • 메멘토 패턴의 주요 목적은 원본 객체의 상태를 캡슐화하여 외부에서 직접 접근하지 않고도 저장하고 복원할 수 있게 하는 것

 

https://reactiveprogramming.io/blog/en/design-patterns/memento

 

주요 구성 요소

 

1. Originator

  • 현재 상태를 저장하고 복원할 객체

 

2. Memento

  • 원본 객체의 상태를 저장하는 객체

 

3. Caretaker

  • 메멘토 객체를 관리하며 필요시 원본 객체의 상태를 복원

 

메멘토 패턴 구현 예시

  • Originator 객체의 상태를 여러 번 변경하고, 상태를 메멘토 객체로 저장한 후, 나중에 이를 복원하는 과정

 

1. Originator

 

public class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public Memento saveStateToMemento() {
return new Memento(state);
}
public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
view raw .java hosted with ❤ by GitHub

 

2. Memento

 

public class Memento {
private final String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
view raw .java hosted with ❤ by GitHub

 

3. Caretaker

 

public class Caretaker {
private List<Memento> mementoList = new ArrayList<>();
public void add(Memento state) {
mementoList.add(state);
}
public Memento get(int index) {
return mementoList.get(index);
}
}
view raw .java hosted with ❤ by GitHub

 

4. 클라이언트 코드

 

public class Client {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("상태 #1");
originator.setState("상태 #2");
caretaker.add(originator.saveStateToMemento());
originator.setState("상태 #3");
caretaker.add(originator.saveStateToMemento());
originator.setState("상태 #4");
System.out.println("현재 상태: " + originator.getState());
originator.getStateFromMemento(caretaker.get(0));
System.out.println("첫 번째 상태 복원: " + originator.getState());
originator.getStateFromMemento(caretaker.get(1));
System.out.println("두 번째 상태 복원: " + originator.getState());
}
}
view raw .java hosted with ❤ by GitHub

 

메멘토 패턴 장단점

 

장점

  • 메멘토 패턴을 사용하면 객체의 내부 상태를 외부에서 직접 접근하지 않고도 저장하고 복원할 수 있으며 이는 객체의 캡슐화를 유지하면서도 객체의 상태를 관리할 수 있게 지원
  • 객체 상태 저장 및 복원하는 역할을 Caretaker에게 위임할 수 있음
  • 객체 상태가 바뀌어도 클라이언트 코드는 변경되지 않음 (SOLID의 OCP 원칙)

 

단점

  • 많은 정보를 저장하는 Mementor를 자주 생성할수록 메모리 사용량에 많은 영향을 줄 수 있음
  • 객체의 모든 상태를 메멘토에 저장해야 하는데 객체의 상태가 복잡하거나 많은 경우 구현이 복잡해질 수 있음

 

실무에서 쓰이는 메멘토 패턴

 

1. java.io.Serializable

  • java.io.Serializable 인터페이스는 자바에서 객체를 직렬화할 수 있도록 해주는 인터페이스
  • 직렬화는 객체의 상태를 저장하거나 전송하기 위해 객체를 바이트 스트림으로 변환하는 과정
  • 메멘토 패턴과 Serializable을 결합하면, 객체의 상태를 보다 쉽게 저장하고 복원할 수 있으며 이는 메멘토 패턴의 주요 목적과 일치

 

class Originator implements Serializable {
private static final long serialVersionUID = 1L;
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public Memento saveStateToMemento() {
return new Memento(state);
}
public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
// 내부 클래스로 Memento 구현
public static class Memento implements Serializable {
private static final long serialVersionUID = 1L;
private final String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
}
class Caretaker {
public void saveMemento(Originator.Memento memento, String filename) throws IOException {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename))) {
oos.writeObject(memento);
}
}
public Originator.Memento loadMemento(String filename) throws IOException, ClassNotFoundException {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename))) {
return (Originator.Memento)ois.readObject();
}
}
}
public class Client {
public static void main(String[] args) {
try {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("상태 #1");
System.out.println("현재 상태: " + originator.getState());
Originator.Memento memento1 = originator.saveStateToMemento();
caretaker.saveMemento(memento1, "memento1.ser");
originator.setState("상태 #2");
System.out.println("현재 상태: " + originator.getState());
Originator.Memento memento2 = originator.saveStateToMemento();
caretaker.saveMemento(memento2, "memento2.ser");
// 상태 복원
Originator.Memento restoredMemento1 = caretaker.loadMemento("memento1.ser");
originator.getStateFromMemento(restoredMemento1);
System.out.println("첫 번째 상태 복원: " + originator.getState());
Originator.Memento restoredMemento2 = caretaker.loadMemento("memento2.ser");
originator.getStateFromMemento(restoredMemento2);
System.out.println("두 번째 상태 복원: " + originator.getState());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
view raw .java hosted with ❤ by GitHub

 

참고

코딩으로 학습하는 GoF의 디자인 패턴 - 백기선 강사님

반응형