open:memento-pattern

Memento Pattern

사용자 채드 보그(Chad Bogue)가 이틀 동안 작성했던 메시지를 날려 버렸다. 그를 위해 저장 버튼을 구현하라.

페드로: 이틀에 걸쳐 텍스트 박스에 타이핑할 수 있는 사람이 있다니 믿기지가 않네요. 이틀이라니!

이브: 그를 구합시다.

페드로: 이 문제로 구글 검색을 해 보니, 저장 버튼을 구현하는 가장 일반적인 접근법은 메멘토 패턴이네요. originator와 caretaker, memento 객체가 필요해요.

이브: 그것이 다 뭐죠?

페드로: originator는 저장하기 원하는 객체 또는 상태예요. 예를 들면, 텍스트 박스 안의 텍스트를 말해요. caretaker는 상태를 저장하는 일을 맡아요. 예를 들어, 저장 버튼이죠. 그리고 memento는 상태를 보관하는 객체이고요.

public class TextBox {
  // state for memento
  private String text = "";

  // state not handled by memento
  private int width = 100;
  private Color textColor = Color.BLACK;

  public void type(String s) {
    text += s;
  }

  public Memento save() {
    return new Memento(text);
  }

  public void restore(Memento m) {
    this.text = m.getText();
  }

  @Override
  public String toString() {
    return "[" + text + "]";
  }
}

페드로: memento는 불변 객체일 뿐이죠.

public final class Memento {
  private final String text;

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

  public String getText() {
    return text;
  }
}

페드로: 그리고 다음의 코드가 caretaker 역할을 합니다.

// open browser, init empty textbox
TextBox textbox = new TextBox();

// type something into it
textbox.type("Dear, Madonna\n");
textbox.type("Let me tell you what ");

// press button save
Memento checkpoint1 = textbox.save();

// type again
textbox.type("song 'Like A Virgin' is about. ");
textbox.type("It's all about a girl...");

// suddenly browser crashed, restart it, reinit textbox
textbox = new TextBox();

// but it's empty! All work is gone!
// not really, you rollback to last checkpoint
textbox.restore(checkpoint1);

페드로: 참고로, 여러 번 저장할 수 있도록 하려면 메멘토들을 리스트에 저장하면 되지요.

이브: originator, caretaker, memento - 필요한 게 너무 많네요. 하지만 실질적으로는 save와 restore 함수만 있으면 충분해요.

(def textbox (atom {}))

(defn init-textbox []
 (reset! textbox {:text ""
                  :color :BLACK
                  :width 100}))

(def memento (atom nil))

(defn type-text [text]
  (swap! textbox
    (fn [m]
      (update-in m [:text] (fn [s] (str s text))))))

(defn save []
  (reset! memento (:text @textbox)))

(defn restore []
  (swap! textbox assoc :text @memento))

이브: 그리고 다음은 실행 예고요.

(init-textbox)
(type-text "'Like A Virgin' ")
(type-text "it's not about this sensitive girl ")
(save)
(type-text "who meets nice fella")
;; crash
(init-textbox)
(restore)

페드로: 거의 동일한 코드네요.

이브: 그래요, 하지만 메멘토가 불변값이어야 한다는 것은 주의해야 해요.

페드로: 무슨 의미죠?

이브: 이 예제에서 자바 불변 String 객체를 다룬 것은 운이 좋은 경우예요. 하지만 내부 상태가 변할 수 있는 가변 객체를 다루는 경우에는, 메멘토 객체를 대상으로 깊은 복사(deep copuy)를 수행해 줄 필요가 있거든요.

페드로: 예, 맞아요. 프로토타입을 얻기 위해 재귀적으로 clone()을 호출해 주어야 하죠.

이브: 프로토타입 패턴은 잠시 뒤에 이야기하겠지만, 메멘토 패턴은 저장(save)과 복원(restore)에 관련된 것이지, originator와 caretaker와 관련된 것은 아니라는 것을 기억해 두셔야 해요.


  • open/memento-pattern.txt
  • 마지막으로 수정됨: 2021/11/21 11:41
  • 저자 127.0.0.1