# 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()을 호출해 주어야 하죠. 이브: [[Prototype Pattern|프로토타입 패턴]]은 잠시 뒤에 이야기하겠지만, 메멘토 패턴은 저장(save)과 복원(restore)에 관련된 것이지, originator와 caretaker와 관련된 것은 아니라는 것을 기억해 두셔야 해요.