# 설명
메멘토(Memento)는 기억의 증표라는 뜻을가지고 있다. 즉 메멘토 패턴을 이용해서 한순간의 기억을 저장(snapshot)했다가 복구(restore)할 수 있다.
복사한 인스턴스를 바꿔주는 것이 아닌, 인스턴스의 상태를 메멘토(Memento)로써 기억하게 한다. 인스턴스를 복원하려면 내부 정보에 자유롭게 접근해야하지만, 그렇게 되면 캡슐화의 원칙이 깨질 위험이 있다. 오직 메멘토로 하여금 이러한 복원작업을 할 수 있게 보증해야한다.
- Originator : 우리가 사용하고 있는 현재 상태의 객체이다. save와 restore와 같은 기능을 Memento를 통해서 수행하며 state의 상태가 이에 따라서 바뀐다. Originator의 state의 은닉을 유지하고, 상태를 저장 복구 할 수 있다.
- Memento : Originator 객체의 내부 State에서 필요한 State를 저장한다.
- CareTaker : Memento의 보관을 책임진다.
복잡한 Originator 클래스의 내부 상태를 다른 객체(Memento)로 분리함으로써 상태에 대한 캡슐화를 보장할 수 있고 복구에 필요한 (클라이언트가 요구하는) 상태만 따로 관리함으로써, Originator 내부에서 저장하지 않고 Originator가 단순해질수 있다.
# 예제
- GOF의 유명한 예제인 주사위(DICE)예제를 살펴보겠다.
- Gamer클래스를 통해서 게임을 시작하고 원하는 횟수만큼 bet을 수행한다.
- bet을 통해 주사위가 던져지면 특정수가 나오면 money가 두배가 되며, 특정수가 나오면 money가 반토막(/2)이 된다.
- Gamer는 money와 Fruits를 가지고 있다. 또한 메멘토를 통해서 해당 State의 undo, restore기능을 제공한다.
- 클라이언트에서 게임이 시작되고, 메멘토보다 money가 작아지면 그 전 상태로 복구, memento보다 돈이 많아지면 memento에 현재 상태를 복사한다.
# 코드
## Gamer
public class Gamer {
private int money;
private ArrayList<String> fruits = new ArrayList();
private Random random = new Random();
private static String[] fruitNames = {
"사과", "포도", "바나나", "귤",
};
public Gamer(int money) {
this.money = money;
}
public int getMoney() {
return money;
}
// 베팅...게임 진행한다.
public void bet() {
// 주사위를 던진다.
int dice = random.nextInt(6) + 1;
if (dice == 1) {
// 1인 경우, 소지금 증가한다.
money += 100;
System.out.println("소지금이 증가하였습니다.");
} else if (dice == 2) {
// 2인 경우, 소지금이 절반이 된다.
money /= 2;
System.out.println("소지금이 절반이 되었습니다.");
} else if (dice == 6) {
// 6인 경우, 과일을 받는다.
String fruit = getFruit();
System.out.println("과일(" + fruit + ")을 받았습니다.");
fruits.add(fruit);
} else {
// 그외인 경우, 아무 일도 일어나지 않는다.
System.out.println("아무 일도 일어나지 않았다.");
}
}
// 스냅샷 생성: 현재 상황을 저장한 객체를 생성하고 반환한다.
public Memento createMemento() {
Memento memento = new Memento(this.money);
for( String fruit : fruits) {
if(fruit.startsWith("맛있는")){
memento.addFruit(fruit);
}
}
return memento;
}
// 실행 취소(Undo)을 실행: 저장했던 객체를 전달받아 이전 상태로 되돌린다.
public void restoreMemento(Memento memento) {
this.money = memento.getMoney();
this.fruits = memento.getFruits();
}
public String toString() {
return "[money = " + money + ", fruits = " + fruits + "]";
}
private String getFruit() {
String prefix = "";
if (random.nextBoolean()) {
prefix = "맛있는 ";
}
return prefix + fruitNames[random.nextInt(fruitNames.length)];
}
}
## Memento
public class Memento {
private int money;
private ArrayList<String> fruits;
Memento(int money){
this.money = money;
fruits = new ArrayList<String>();
}
int getMoney(){
return money;
}
void addFruit(String fruit) {
fruits.add(fruit);
}
ArrayList<String> getFruits() {
return (ArrayList<String>) fruits.clone();
}
}
## Client
public class Client {
public static void main(String[] args) {
// 첫 소지금은 100이다.
Gamer gamer = new Gamer(100);
// 첫번째 상태를 저장한다.
Memento memento = gamer.createMemento();
ArrayList<Memento> careTaker = new ArrayList<Memento>();
// 초기 상태를 careTaker에 등록해놓습니다.
careTaker.add(memento);
for (int i = 0; i < 10; i++) {
System.out.println("==== " + i);
System.out.println("현재 상태:" + gamer);
// 게임을 진행한다.
gamer.bet();
System.out.println("소지금은 " + gamer.getMoney() + "원이 되었습니다.");
if( gamer.getMoney() < memento.getMoney() /2 ){
System.out.println(" ( 파산을 막기위해 이전 상태로 복구합니다. ) ");
gamer.restoreMemento(memento);
}else if ( gamer.getMoney() >= memento.getMoney()){
System.out.println(" ( 돈이 많아서 현재 상태를 SAVE 합니다. ) ");
memento = gamer.createMemento();
careTaker.add(memento);
}
// 천천히 플레이 될 수 있도록
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
System.out.println("");
}
System.out.println();
System.out.println("memento 상황 조회");
for( Memento m : careTaker) {
System.out.println(m.getMoney());
}
}
}
# 실행 결과
# 참고
'• 독서 > Design Pattern' 카테고리의 다른 글
[디자인패턴] 중재자 패턴(Mediator Pattern) (0) | 2022.10.11 |
---|---|
[디자인패턴] 퍼사드 패턴(Facade Pattern) (0) | 2022.10.11 |
[디자인 패턴] 옵저버 패턴(Observer Pattern) (1) | 2022.10.04 |
[디자인 패턴] 스테이트 패턴(State Pattern) (0) | 2022.10.02 |
[디자인패턴] 어댑터 패턴(Adapter Pattern) (0) | 2022.10.01 |