
의미
: Memento라는 단어 자체는 '기념품, 유품, 추억거리' 등을 의미하며, 메멘토 패턴은 어느 시점에서 인스턴스 상태를 확실하게 기록하여 저장하고 나중에 해당 시점으로 돌리기 위해 사용된다.
필요한 이유
: 인스턴스를 복원하기 위해서는 인스턴스 내부 정보에 자유롭게 접근할 수 있어야 한다. -> 캠슐화의 파괴로 이어질 수 있다.

package game;
import java.util.ArrayList;
import java.util.List;
public class Memento {
private int money;
private List<String> fruits;
public int getMoney() {
return money;
}
Memento(int money) {
this.money = money;
this.fruits = new ArrayList<>();
}
void addFruit(String fruit) {
fruits.add(fruit);
}
List<String> getFruits() {
return new ArrayList<>(fruits);
}
}
Memento)와 addFruit 메서드의 접근 제한자에 public이 없기 때문에 동일 패키지에 속한 클래스에서만 사용할 수 있다.package game;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Gamer {
private int money;
private List<String> fruits = new ArrayList<>();
private Random = new Random();
private static String[] fruitsName = {
"사과", "포도", "바나나", "오렌지",
};
public Gamer(int money) {
this.money = money;
}
public int getMoney() {
return money;
}
public void bet() {
int dice = random.nextInt(6) + 1;
if (dice == 1) {
money += 100;
System.out.println("소지금이 증가했습니다.");
} else if (dice == 2) {
money /= 2;
System.out.println("소지금이 절반으로 줄었습니다.");
} else if (dice == 6) {
String f = getFruit();
System.out.println("과일(" + f + ")를 받았습니다.");
fruits.add(f);
} else {
System.out.println("변동 사항이 없습니다.");
}
}
public Memento createMemento() {
Memento m = new Memento(money);
for (String f: fruits) {
if (f.startsWith("맛있는 ")) {
m.addFruit(f);
}
}
return m;
}
public void restoreMemento(Memento memento) {
this.money = memento.getMoney();
this.fruits = memento.getFruits();
}
@Override
public String toString() {
return "[money = " + money + ", fruits = " + fruits + "]";
}
private String getFruit() {
String f = fruitsName[random.nextInt(fruitsName.length)];
if (random.nextBoolean()) {
return "맛있는 " + f;
} else {
return f;
}
}
}
import game.Memento;
import game.Gamer;
public class Main {
public static void main(String[] args) {
Gamer gamer = new Gamer(100);
Memento memento = gamer.createMemento();
for (int i = 0; i < 100; i++) {
System.out.println("==== " + i);
System.out.println("상태:" + gamer);
gamer.bet();
System.out.println("소지금은 " + gamer.getMoney() + "원이 되었습니다.");
if (gamer.getMoney() > memento.getMoney()) {
System.out.println("많이 늘었으니 현재 상태를 저장하자!");
memento = gamer.createMemento();
} else if (gamer.getMoney() < memento.getMoney() / 2) {
System.out.println("많이 줄었으니 현재 상태를 복원하자!");
gamer.restoreMemento(memento);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println();
}
}
}




Originator (Gamer 클래스)
자신의 현재 상태를 저장하고 싶을 때 Memento를 만들고 이전 Memento를 넘겨받으면 그 Memento를 만든 시점으로 상태를 되돌리는 처리를 한다.
Memento
Originator의 내부 정보를 가지고 있으면서도 누구에게나 공개하지는 않는다.
wide interface : 오브젝트의 상태를 되돌리는 데 필요한 정보를 모두 얻을 수 있는 메소드의 집합. Memento의 내부 상태를 드러내기 때문에 Originator만 사용할 수 있다.narrow interface : 외부 Caretaker에 보여지는 인터페이스캡슐화 파괴를 막을 수 있다. (내부 상태를 공개할 대상이 정해지기 때문)Originator 클래스는 Memento를 만드는 일, 주어진 Memento로 자신의 상태를 되돌리는 일을, Caretaker는 어느 시점에 스냅샷을 찍을지나 언제 실행 취소를 할지를 결정하는 일을 담당한다.
Memento 클래스 예제에서 wide / narrow interface
package game;
import java.util.ArrayList;
import java.util.List;
public class Memento {
private int money;
private List<String> fruits;
// narrow interface
public int getMoney() {
return money;
}
// wide interface
Memento(int money) {
this.money = money;
this.fruits = new ArrayList<>();
}
// wide interface
void addFruit(String fruit) {
fruits.add(fruit);
}
// wide interface
List<String> getFruits() {
return new ArrayList<>(fruits);
}
}
public키워드가 붙은 메서드가 narrow interface이고 main에서 접근 가능
public이 붙지 않은 메서드는 같은 패키지인 Gamer에서만 접근 가능하며 wide interface
// main 함수에서 getMoney 접근 가능
if (gamer.getMoney() > memento.getMoney()) {
System.out.println("많이 늘었으니 현재 상태를 저장하자!");
memento = gamer.createMemento();
} else if (gamer.getMoney() < memento.getMoney() / 2) {
System.out.println("많이 줄었으니 현재 상태를 복원하자!");
gamer.restoreMemento(memento);
}
public 붙은 메서드에 '좁다'는 표현을 사용하는 이유는 '내부 상태를 조작할 수 있는 정도가 적다'는 의미