PostCover

디자인 패턴을 쉽게 풀어보자 - 메멘토 패턴편

GoF의 23가지 디자인 패턴 중 하나인 '메멘토 패턴(Memento Pattern)'을 쉽게 풀어보기

메멘토(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());  // (빈 문자열)
    }
}

메멘토 패턴의 동작 원리

  1. Originator(원본 객체): 자신의 상태를 메멘토 객체에 저장하고 복원합니다.
  2. Memento(메멘토 객체): Originator의 상태를 캡슐화합니다.
  3. Caretaker(관리자 객체): 메멘토 객체를 저장하고 필요할 때 복원합니다.

메멘토 패턴의 장단점

장점

  • 캡슐화 유지: 객체의 내부 상태를 외부에 노출하지 않고 저장하고 복원할 수 있습니다.
  • Undo/Redo 기능 구현 용이: 여러 단계의 상태를 관리할 수 있습니다.
  • 유연성 향상: 객체의 상태가 변경되더라도 클라이언트 코드에 영향을 미치지 않습니다.

단점

  • 메모리 사용 증가: 모든 상태를 메멘토 객체로 저장하면 메모리 사용량이 늘어날 수 있습니다.
  • 복잡성 증가: 객체의 상태가 복잡할수록 메멘토 패턴을 구현하는 코드도 복잡해집니다.
  • Caretaker 관리 부담: 여러 메멘토를 관리해야 하는 Caretaker의 역할이 복잡해질 수 있습니다.

결론

메멘토 패턴은 객체의 상태를 안전하게 저장하고 복원하는 데 유용한 패턴입니다. 이 패턴은 Undo/Redo 기능과 같이 여러 단계로 상태를 되돌릴 수 있는 기능이 필요한 경우에 적합합니다.

하지만 메모리 사용량이 늘어날 수 있으므로, 메멘토 객체의 사용을 신중히 관리해야 합니다. 텍스트 편집기, 게임 상태 저장, 트랜잭션 관리와 같은 애플리케이션에서 자주 사용됩니다.