1. 개요

이 예제에서는 Memento 디자인 패턴이 무엇이며 어떻게 사용하는지 알아봅니다.

먼저 약간의 이론을 살펴보겠습니다. 그런 다음 패턴의 사용법을 설명하는 예제를 만듭니다.

2. 메멘토 디자인 패턴이란?

Gang of Four가 저서에서 설명한 Memento 디자인 패턴 은 행동 디자인 패턴입니다. Memento 디자인 패턴은 취소할 수 있는 작업을 구현하는 솔루션을 제공합니다. 주어진 순간에 개체의 상태를 저장하고 이후 수행된 작업을 실행 취소해야 하는 경우 복원함으로써 이를 수행할 수 있습니다.

실제로 상태를 저장해야 하는 개체를 Originator라고 합니다. Caretaker는 Memento라고 하는 상태의 저장 및 복원을 트리거하는 개체입니다.

Memento 개체는 Caretaker에 가능한 한 적은 정보를 노출해야 합니다. 이는 캡슐화 원칙을 깨뜨리기 때문에 Originator의 내부 상태를 외부 세계에 노출하지 않도록 하기 위한 것입니다. 그러나 Originator는 원래 상태로 복원하기 위해 충분한 정보에 액세스해야 합니다.

서로 다른 개체가 서로 상호 작용하는 방식을 보여주는 빠른 클래스 다이어그램을 살펴보겠습니다.

메멘토 디자인 패턴 1

보시다시피 Originator는 Memento를 생산하고 소비할 수 있습니다. 한편 Caretaker는 상태를 복원하기 전에만 상태를 유지합니다. Originator의 내부 표현은 외부 세계에서 숨겨져 있습니다.

여기에서는 단일 필드를 사용하여 Originator의 상태를 나타내지만 하나의 필드로 제한되지 않고 필요한 만큼 많은 필드를 사용할 수 있습니다 . 또한 Memento 개체에 있는 상태는 Originator의 전체 상태와 일치할 필요가 없습니다. 보관된 정보가 Originator의 상태를 복원하기에 충분하다면 계속 진행해도 됩니다.

3. Memento 디자인 패턴은 언제 사용합니까?

일반적으로 Memento 디자인 패턴은 일부 작업을 실행 취소할 수 없어 이전 상태로 롤백해야 하는 상황에서 사용됩니다. 그러나 Originator의 상태가 무거운 경우 Memento 디자인 패턴을 사용하면 비용이 많이 드는 생성 프로세스와 메모리 사용량이 증가할 수 있습니다.

4. 메멘토 패턴의 예

4.1. 초기 샘플

이제 Memento 디자인 패턴의 예를 살펴보겠습니다. 텍스트 편집기가 있다고 상상해 봅시다.

public class TextEditor {

    private TextWindow textWindow;

    public TextEditor(TextWindow textWindow) {
        this.textWindow = textWindow;
    }
}

여기에는 현재 입력된 텍스트를 보유하고 더 많은 텍스트를 추가할 수 있는 방법을 제공하는 텍스트 창이 있습니다.

public class TextWindow {

    private StringBuilder currentText;

    public TextWindow() {
        this.currentText = new StringBuilder();
    }

    public void addText(String text) {
        currentText.append(text);
    }
}

4.2. 기념물

이제 텍스트 편집기가 일부 저장 및 실행 취소 기능을 구현하기를 원한다고 상상해 봅시다. 저장할 때 현재 텍스트를 저장하려고 합니다. 따라서 후속 변경을 취소하면 저장된 텍스트가 복원됩니다.

이를 위해 Memento 디자인 패턴을 사용합니다. 먼저 창의 현재 텍스트를 포함하는 개체를 만듭니다.

public class TextWindowState {

    private String text;

    public TextWindowState(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}

이 개체는 우리의 Memento입니다. 보시다시피 우리는 외부인이 현재 텍스트를 업데이트하는 것을 방지하기 위해 StringBuilder 대신  String 을 사용하기로 선택했습니다.

4.3. 창작자

그런 다음  TextWindow 클래스에 Memento 개체를 만들고 소비하는 메서드를 제공하여 TextWindow 를 Originator로 만들어야 합니다.

private StringBuilder currentText;

public TextWindowState save() {
    return new TextWindowState(currentText.toString());
}

public void restore(TextWindowState save) {
    currentText = new StringBuilder(save.getText());
}

save() 메서드  를 사용하면 개체를 만들 수 있고,  restore() 메서드는 개체를 사용하여 이전 상태를 복원합니다.

4.4. 돌보는 사람

마지막으로 TextEditor 클래스를 업데이트해야 합니다. Caretaker는 Originator의 상태를 유지하고 필요할 때 복원하도록 요청합니다.

private TextWindowState savedTextWindow;

public void hitSave() {
    savedTextWindow = textWindow.save();
}

public void hitUndo() {
    textWindow.restore(savedTextWindow);
}

4.5. 솔루션 테스트

샘플 실행을 통해 작동하는지 확인해보자. 편집기에 텍스트를 추가하고 저장한 다음 더 추가하고 마지막으로 실행 취소한다고 상상해 보십시오. 이를 달성하기 위해 TextEditor 에 현재 텍스트 의 문자열 을 반환하는  print()  메서드를  추가합니다.

TextEditor textEditor = new TextEditor(new TextWindow());
textEditor.write("The Memento Design Pattern\n");
textEditor.write("How to implement it in Java?\n");
textEditor.hitSave();
 
textEditor.write("Buy milk and eggs before coming home\n");
 
textEditor.hitUndo();

assertThat(textEditor.print()).isEqualTo("The Memento Design Pattern\nHow to implement it in Java?\n");

보시다시피 마지막 문장은 Memento가 추가되기 전에 저장되었기 때문에 현재 텍스트의 일부가 아닙니다.

5. 결론

이 짧은 기사에서는 Memento 디자인 패턴과 그 용도에 대해 설명했습니다. 또한 간단한 텍스트 편집기에서 사용법을 설명하는 예제도 살펴보았습니다.

이 기사에 사용된 전체 코드는 GitHub 에서 찾을 수 있습니다 .

Generic footer banner