[디자인패턴] Memento

Judy·2022년 12월 14일
0

디자인패턴

목록 보기
8/11
post-thumbnail

Memento

Begavioral Patterns

구현 세부정보를 공개하지 않고 객체의 이전 상태를 복원할 수 있는 패턴


문제

텍스트 편집기 앱을 상상해봅시다. 텍스트 수정 외에도 서식 지정, 이미지 삽입 등을 수행할 수 있습니다. 그럼 실행 취소는 어떨까요?

실행 취소를 구현하기 위해 작업을 수행하기 전에 모든 상태를 일부 저장소에 저장하고 나중에 작업을 되돌릴 때 최신 스냅샷을 가져와 모든 상태를 복원하게 됩니다.

하지만 모든 상태를 저장하기 위해선 모든 컨텐츠에 대한 접근 제한을 완화해야 합니다. 불행하게도 대부분의 필드는 외부에서 볼 수 없도록 비공개 상태입니다.

모두 공개를 했다고 가정해도 문제가 남아있습니다. 나중에 편집기 클래스를 리팩토링할 때 상태 복사를 담당하는 클래스 역시 변경해야 할 수 있습니다.

상태 스냅샷은 텍스트, 커서 좌표, 스크롤 위치 등 많은 값을 컨테이너에 넣어야 합니다. 보통 이런 컨테이너 역시 한 클래스의 객체를 사용하고 해당 클래스의 필드 역시 공개해야 합니다. 결국 편집기의 상태가 노출되어 편집기와 관련된 사항들에 취약하게 됩니다.

이러한 문제를 직면하지 않고 undo를 구현할 수 있는 방법은 없을까요?

해결

손상된 캡슐화가 문제❗️ 데이터를 수집하기 위해 다른 객체의 private 공간을 침범하게 됩니다.

Memento 패턴은 상태 스냅샷 생성을 해당 상태를 소유한 객체에 위임합니다. 외부에서 해당 객체에 상태를 복사하는 대신 편집기 클래스 자체가 스냅샷을 만들게 됩니다.

객체의 상태는 memento라는 특수 객체에 저장하도록 합니다. memento는 해당 객체를 생성한 객체를 제외하고는 접근할 수 없습니다. 대신 스냅샷의 메타데이터(생성 시간, 수행 작업 이름 등)를 인터페이스를 사용해 memento에서 가져올 수 있습니다.

이러한 제한을 이용하면 caretaker이라는 다른 객체에 memento를 저장할 수 있습니다. caretaker는 인터페이스를 통해서만 memento를 관리하기 때문에 memento 내부 상태는 변경할 수 없습니다. 또한 originator(= 편집기)는 memento 내부 필드에 접근할 수 있어 이전 상태를 복원할 수 있습니다.

편집기 앱 예제에서 caretaker 역할을 하는 클래스를 만들 수 있습니다. 사용자가 실행 취소를 트리거하면 가장 최근의 memento를 가져와 편집기로 전달하며 롤백을 요청합니다.

구조

1. Originator

  • 자체 상태의 스냅샷을 생성 및 복원할 수 있음

2. Memento

  • Originator 상태의 스냅샷 역할을 하는 값 객체
  • 불변으로 만들고 생성자를 통해 데이터를 한 번만 전달하는게 일반적
  • Originator의 중첩?? 생성자가 비공개여도 memento 필드와 메서드에 접근 가능

3. Caretaker

  • Originator의 상태를 언제, 캡쳐해야 하는지와 복원 시기를 알고 있음
  • 필드로 Originator와 Memento 스택을 가짐
  • 실행 취소를 해야할 때 스택 맨 위 memento를 가져와 Originator의 복원 메서드로 전달
  • memento 필드와 메서드에 제한적으로 접근 가능 (스택에 memento를 저장할 수 있지만 상태 변경은 ❌)

적용

  • 이전 상태를 복원할 수 있도록 객체 상태의 스냅샷을 생성하고 싶을 때
  • 객체의 필드/getter/setter에 대한 직접 접근이 캡슐화를 위반하는 경우

구현 방법

  1. Originator 역할을 할 객체를 결정
  2. Memento 타입을 생성. Originator 내부 필드를 미러링하는 필드 집합을 선언.
  3. Memento를 불변 객체로 만들기. 생성자를 통해 데이터를 한 번만 적용해야 함
  4. Nested classes를 지원하는 경우 Originator에 중첩하고 아니면 Memento의 빈 인터페이스를 추출해 Originator가 참조하도록 함
  5. Originator에 Memento 객체를 생성하는 메서드를 구현
  6. Originator에 Memento를 매개변수로 받아 상태를 복원하는 메서드 구현
  7. Caretaker는 Originator에게 새로운 상태를 저장하게 하는 시기와 복원하는 시기를 알아야 함
  8. Caretaker와 Originator는 Memento를 통해 연결

장단점

✅ 장점

  • 캡슐화를 위반하지 않고 객체의 상태의 스냅샷을 생성할 수 있음
  • caretaker가 originator의 상태 기록을 유지해 originator의 코드를 단순화 가능

❎ 단점

  • 너무 자주 memento를 생성하면 앱에서 많은 RAM을 사용할 수 있음
  • caretaker가 오래된 memento는 폐기할 수 있도록 originator의 lifecycle을 추적해야 함
  • PHP, Python 및 JavaScript와 같은 동적 프로그래밍 언어는 memento의 상태가 그대로 유지됨을 보장할 수 없음



참고 링크
Memento

profile
iOS Developer

0개의 댓글