반응형
SMALL
이번 학습 주제는
추억거리(Memento) 패턴입니다
문서를 작성할 때
필요한 글을 실수로 삭제하게 되면
undo(실행취소) 기능을 사용합니다
객체 지향의 프로그램에서
이 기능을 실행하려면
인스턴스가 가진
정보를 저장해야하고
인스턴스 내부 정보에
접근 가능해야합니다
하지만 접근의 허용은
자칫 캡슐화의 파괴를
초래할 수 있습니다
Memento 패턴은
캡슐화의 파괴에 빠지지 않고도
할 수 있도록 해줍니다
역할 | 설명 |
Originator(작성자) | 자신의 현재 상태를 저장하고 싶을 때 Memento 역할을 생성하고 이전의 Memento 역할을 전달 받으면 그 Memento 역할을 만든 시점의 상태로 돌리는 처리를 실행 |
Memento(추억거리) | Originator 역할의 내부 정보를 정리하며 그 정보는 비공개함 Memento 역할은 두 종류의 인터페이스를 가짐 1. Wide Interface 오브젝트의 상태를 원래의 상태로 돌리기 위해 필요한 정보를 모두 얻을 수 있는 메소드의 집합으로 Originator 역할만 사용 가능 2. Narrow Interface 외부의 Caretaker 역할에게 보여주는 것으로 할 수 있는 일의 한계가 있으며 내부 상태가 외부에 공개되는 것을 방지 |
Caretaker(관리인) | Memento 역할을 한 덩어리의 블랙박스로서 통째로 저장 |
이번 역할들은 다소 설명이 기네요-_-
구현을 해봐야 감을
잡을 수 있을 것 같네요
1. 클래스(Class) Memento.java
package mementoPattern.game;
import java.util.*;
public class Memento {
int money;
ArrayList<String> fruits;
public int getMoney() {
return money;
}
Memento(int money) {
this.money = money;
this.fruits = new ArrayList<String>();
}
void addFruit(String fruit) {
fruits.add(fruit);
}
List<String> getFruits() {
return (List<String>)fruits.clone();
}
}
2. 클래스(Class) Gamer.java
package mementoPattern.game;
import java.util.*;
public class Gamer {
private int money;
private List<String> fruits = new ArrayList<String>();
private Random random = new Random();
private static String[] fruitsname = {
"Apple", "Banana", "Tangerine", "Grape",
};
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 fruit = getFruit();
System.out.println("과일(" + fruit + ")을 받았습니다");
fruits.add(fruit);
} else {
System.out.println("변한 것이 없습니다");
}
}
public Memento createMemento() {
Memento m = new Memento(money);
Iterator<String> it = fruits.iterator();
while ( it.hasNext() ) {
String fruit = (String) it.next();
if ( fruit.startsWith("맛있는") ) {
m.addFruit(fruit);
}
}
return m;
}
public void restoreMemento(Memento memento) {
this.money = memento.money;
this.fruits = memento.getFruits();
}
public String toString() {
return "[money = " + money + ", fruits = " + fruits + "]";
}
private String getFruit() {
String prefix = "";
if ( random.nextBoolean() ) {
prefix = "맛있는";
}
return prefix + fruitsname[random.nextInt(fruitsname.length)];
}
}
3. 클래스(Class) Test.java
import mementoPattern.game.*;
public class Test {
public static void main(String[] args) {
/* Memento Pattern */
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 ) {
/* Ignore */
}
System.out.println("");
}
}
}
위의 코드는
메모리 상에서만
Memento를 저장합니다
이것을 파일로
저장하도록 변경하겠습니다
4. 클래스(Class) Memento.java
package mementoPattern.game;
import java.io.*;
import java.util.*;
public class Memento implements Serializable {
int money;
ArrayList<String> fruits;
public int getMoney() {
return money;
}
Memento(int money) {
this.money = money;
this.fruits = new ArrayList<String>();
}
void addFruit(String fruit) {
fruits.add(fruit);
}
List<String> getFruits() {
return (List<String>)fruits.clone();
}
}
5. 클래스(Class) Test.java
import mementoPattern.game.*;
import java.io.*;
import java.util.zip.*;
public class Test {
public static final String SAVEFILENAME = "game.dat";
public static void main(String[] args) {
/* Memento Pattern */
Gamer gamer = new Gamer(100);
Memento memento = loadMemento();
if ( memento != null ) {
System.out.println("지난번 저장한 결과에서 시작합니다");
gamer.restoreMemento(memento);
} else {
System.out.println("새로 시작합니다");
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();
saveMemento(memento);
} else if ( gamer.getMoney() < memento.getMoney() / 2 ) {
System.out.println(" (많이 감소했으므로 이전의 상태로 복원하자) ");
gamer.restoreMemento(memento);
}
try {
Thread.sleep(1000);
} catch ( InterruptedException e ) {
/* Ignore */
}
System.out.println("");
}
}
public static void saveMemento(Memento memento) {
try {
ObjectOutput out = new ObjectOutputStream(new DeflaterOutputStream(new FileOutputStream(SAVEFILENAME)));
out.writeObject(memento);
out.close();
} catch ( IOException e ) {
e.printStackTrace();
}
}
public static Memento loadMemento() {
Memento memento = null;
try {
ObjectInput in = new ObjectInputStream(new InflaterInputStream(new FileInputStream(SAVEFILENAME)));
memento = (Memento)in.readObject();
in.close();
} catch ( FileNotFoundException e ) {
System.out.println(e.toString());
} catch ( IOException e ) {
e.printStackTrace();
} catch ( ClassNotFoundException e ) {
e.printStackTrace();
}
return memento;
}
}
Gamer 클래스의 변화없이
새로운 기능이 추가되었습니다
이렇게 될 수 있는 이유는
역할 분담이 되어 있기 때문입니다
이번 학습에서는
Serializable과 자바의 파일 압축도
함께 학습할 수 있었네요~
아무튼 이것으로
오늘의 학습을 마치겠습니다
그럼 이만-_-
반응형
LIST
'Java' 카테고리의 다른 글
[Design Pattern] 플라이급 패턴(Flyweight Pattern) (0) | 2020.07.21 |
---|---|
[Design Pattern] 상태 패턴(State Pattern) (0) | 2020.07.20 |
[Design Pattern] 관찰자 패턴(Observer Pattern) (0) | 2020.07.15 |
[Design Pattern] 창구 패턴(Facade Pattern)과 중개자 패턴(Mediator Pattern) (0) | 2020.07.13 |
[Design Pattern] 책임 떠넘기기 패턴(Chain of Responsibility Pattern) (0) | 2020.07.12 |