Download presentation
Presentation is loading. Please wait.
Published byLionel Archambault Modified 6년 전
1
Memento Something writing to Memento Card, keep it in a drawer. When you need to recall it, open drawer and take it back 발표자 김지원
2
Agenda 덕성여대 PT 그대로 GoF C++ 추가 Memento 패턴 예제프로그램 등장역할 독자의 사고를 넓혀주는 힌트
관련패턴 요약 GoF C++ 추가
3
01. Memeto 패턴 undo 기능을 제공하려면 이전의 상태를 누군가가 기억하고 있어야 한다.
Memento 패턴 사용 목적 undo(다시 하기) redo(재실행) history(작업 이력의 작성) snapshot(현재 상태의 보존) memento의 뜻 기념품, 유품, 추억거리
4
02. 예제 프로그램 주사위 게임으로 과일 모으기 게임 규칙
이 게임은 자동으로 진행된다. 게이머가 주사위를 던져 나온 수가 다음 상태를 결정한다. 좋은 수가 나오면 게이머의 돈이 증가한다. 나쁜 수가 나오면 돈이 줄어든다. 특히 좋은 수가 나오면 게이머는 과일을 받는다. 돈이 다 없어지면 종료한다. 돈이 많이 모이면, 장래를 위해 Memento 클래스의 인스턴스를 만들어 ‘현재의 상태(돈과 과일)’를 보존한다. 계속해서 돈이 줄어들어 돈이 반 미만으로 줄면, Memento의 인스턴스를 사용해서 보존해 두었던 이전 상태로 복귀한다.
5
02. 예제 프로그램
6
02. 예제 프로그램
7
02. 예제 프로그램 Memento 클래스 게이머의 상태를 표현하는 클래스 money 필드: 현재 소유한 돈
fruits 필드: 현재 가지고 있는 과일 생성자에 public이 없다 => package 접근 권한 같은 패키지에 속하는 클래스에서만 사용할 수 있다. addFruit(String) 과일을 추가할 때 호출하는 메소드 package 접근 권한 => game 패키지 외부에서는 이 클래스의 내부 상태를 변경할 수 없다. getMoney( )
8
02. 예제 프로그램 Gamer 클래스 게임을 수행하는 게이머를 표현하는 클래스
돈과 과일, 난수 발생기, 과일 이름 배열을 가진다. bet( ) 1이 나오면 돈이 100원 증가한다. 2가 나오면, 돈이 반으로 준다. 6이 나오면, 과일을 받는다. createMemento( ) 현재 상태를 보존하는 Memento 객체를 생성함 현재 돈과 맛있는 과일들 만을 Memento 객체에 보존한다 찰칵하고 사진을 찍듯이 현재의 상태를 Memento 인스턴스에 보존해 두는 것이다.
9
02. 예제 프로그램 Gamer 클래스(계속) restoreMemento( ) getFruit( ) undo를 행하는 메소드
선택적으로 ‘맛있다’를 과일이름 앞에 붙인다.
10
02. 예제 프로그램 Main 클래스 gamer.createMemento( )를 호출하여 현재 gamer의 상태를 보존한다.
11
02. 예제 프로그램
12
02. 예제 프로그램 바둑 게임 수 무르기 & 복기 기능(바둑 둔 수대로 재현하기) 바둑판 상태의 저장 돌 색깔, 위치, …
GoBoard -board_ : int[19][19] -whiteDeadNum_ : int -blackDeadNum_ : int -whoseTurn_ : int -totalStoneNum_ : int -paePosX_ : int -paePosY_ : int +PutStone(x : int, y : int) +RetractStone(cnt : int) +PrintBoard() GoMemento -board_ : int[19][19] -whiteDeadNum_ : int -blackDeadNum_ : int -whoseTurn_ : int -totalStoneNum_ : int -paePosX_ : int -paePosY_ : int #CopyBoard(src : GoMemento&) +GetOutDeadStone() +IsPaePosition() GoBoard -historyList_ : list<GoMemento*> -pCurBoard_ : GoMemento* -whoseTurn_ : int -totalStoneNum_ : int +PutStone(x : int, y : int) +RetractStone(cnt : int) +PrintBoard()
13
02. 예제 프로그램 Class GoMemento { friend class GoBoard; Public :
GoMemento(const GoMemento& rhs) { CopyBoard(rhs);} GoMemento& operator=(const GoMemento& rhs) { CopyBoard(rhs); } void GetOutDeadStone() { // -- 확실히 죽은 돌을 골라낸다. // -- 이때 whiteDeadNum_이나 blackDeadNum_ 값이 조정됨 } bool IsPaePosition(int x, int y){ // -- x, y 위치가 패이면 true 되돌림 return false;
14
02. 예제 프로그램 Protected : void CopyBoard(const GoMemento& src) {
for(int i=0; i< GO_BOARD_SIZE i++) for(int j=0; j< GO_BOARD_SIZE; j++) board_[i][j] = src.board_[i][j]; whileDeadNum_ = src.whiteDeadNum_; blackDeadNum_ = src.blackDeadNum_; } Private : // -- 0은 돌 없음, 양수는 백, 음수는 흑, 절대 값은 수순 int board_[GO_BOARD_SIZE][GO_BOARD_SIZE]; int whiteDeadNum_; // -- 횐 돌 죽은 수 int blackDeadNum_; // -- 검은 돌 죽은 수 int paePosX_; int pawPosY_;
15
02. 예제 프로그램 Class GoBoard { public : GoBoard(int firstTurn = -1) {
pCurBoard_ = new GoMemento(); whoseTurNum_ = 0; } void PutStone(int x, int y) { if(pCurBoard_->board_[x][y] != 0 || (pCurBoard_->pawPosX_ == x && pCurBoard_->pawPosY_ == y)) { cout << “Can’ be Put Stone There.” << endl; return; GoMemento *pNewBoard = new GoMemento(*pCurBoard_); totalStoneNum_ ++; pNewBoard->board_[x][y] = whoseTurn_ * totalStoneNum_; whoseTurn_ *= -1; if (pCurBoard_=>IsPaePosition(x,y)) { pCurBoard_->paePosX_ = x; pCurBoard_->pawPosY_ = y;
16
02. 예제 프로그램 else { pCurBoard_->paePosX_ = -1;
pCurBoard_->paePosY_ = -1; } pNewBoard->GetOutDeadStone(); historyList_.push_front(pCurBoard_); pCurBoard_ = pNewBoard; void RetractStone(int cnt) { // cnt 값만큼 수를 물린다. // 이때 한 수를 물리는 것은 pCurBoard_ 값을 historyList_의 // 첫 항목과 대치하는 것과 동일하다. if (cnt <= 0) return; for(int i=0; i<cnt -1 ; i++){ GoMemento *pTmpBoard = historyList_.front(); delete pTmpBoard; historyList_.pop_front(); totalStoneNum_--;
17
02. 예제 프로그램 delete pCurBoard_; totalStoneNum_--;
if (historyList_.empty()) pCurBoard_ = new GoMemento(); else pCurBoard_ = historyList_.front(); } void PrintBoard(){ // 전체 출력 Private: list<GoMemento*> historyList_; GoMemento* pCurBoard_; int whoseTurn_; int totalStoneNum_; }; Int main(){ GoBoard board; // 일련의 테스트… }
18
03. 등장 역할 Originator(작성자)의 역할 Memento(기념품)의 역할
예제에서는, Gamer 클래스가 해당됨 Memento(기념품)의 역할 Originator 역할의 내부 정보를 보존한다. 예제에서는 Memento 클래스가 해당됨
19
03. 등장 역할 Caretaker(돌보는 사람)의 역할
Originator 역할의 상태를 보존하고 싶을 때 Originator에게 알리는 역할을 담당한다. 예제에서는, Main 클래스가 해당됨 Originator 역할과 Memento 역할은 견고하게 결속되어 있다. Caretaker 역할과 Memento 역할은 느슨하게 연결되어 있다. Caretaker 역할이 Memento 내부의 정보를 쉽게 접근할 수 없다.
20
03. 등장 역할
21
04. 독자의 사고를 넓혀주는 힌트 액세스 제어 Memento 클래스의 생성자는 아무것도 안 붙어 있음
Main 클래스는 Memento의 생성자를 호출할 수 없다. Main 클래스 안에서 Memento 객체를 생성할 수 없다. Memento가 필요할 때 마다, Gamer 클래스의 createMemento를 호출한다.
22
04. 독자의 사고를 넓혀주는 힌트 Memento를 몇 개 가질까?
예제에서는, Main 클래스가 가지고 있는 Memento는 하나뿐이었다. 배열 등을 사용하면 Main이 여러 개의 Memento 인스턴스를 생성해서 가지고 있을 수 있다. 즉, 여러 시점의 상태를 보존해 둘 수가 있다.
23
04. 독자의 사고를 넓혀주는 힌트 Caretaker 역할과 Originator 역할을 분리하는 의미
“undo를 하고 싶으면, Originator 역할에게 그 기능을 만들어 넣으면 되지, 일부러 디자인 패턴을 사용할 필요가 있을까?” Caretaker의 역할 어느 시점에서 스냅샷을 찍어, 언제 undo를 실행할 지 결정함 Memento 역할을 보관하는 일을 함 Originator의 역할 Memento 역할을 생성하는 일을 함 Memento 역할을 사용해서 자신의 상태를 원래대로 되돌리는 일을 함 분리해 두면 좋은 점 다음과 같은 수정을 할 때, Originator 역할은 변경할 필요가 없다. 여러 단계의 undo를 실행하도록 변경하고 싶다 undo만 하는 것이 아니라, 현재의 상태를 파일에 보존하고 싶다.
24
05. 관련 패턴 Command 패턴(22장) Prototype 패턴(6장) State 패턴 (19장)
25
06. 요약 현재 객체의 상태를 기록하여 보존하기 위한 Memento 패턴
Caretaker 역할은 중간 중간 Originator 역할에게 부탁하여 ‘현재의 상태’를 표현하는 Memento 역할을 만든다. Caretaker 역할은 필요할 때 서랍 안에서 Memento 역할을 꺼내서 Originator 역할에게 건네주면, 그 상태로 복원이 된다.
26
유용한 점 어떤 객체의 특정 시점에 대한 상태정보가 나중에 복원되기 위해 저장되어야 할 경우
객체의 상태를 직접 접근하는 것이 해당 객체의 구현 상 너무 복잡하거나 정보 은닉을 깨뜨릴 경우
27
연습 문제 18-1 Memento의 getMoney( )만 public으로 선언되어 있어, 외부에서 이 메소드를 통해서만 내부 정보를 접근할 수 있다. Memento의 다른 메소드들도 public으로 선언되어, Caretaker 역할이 Memento 역할을 자유롭게 조작할 수 있다면 어떤 불편함이 있을까? 18-2 Memento가 보존해야 할 정보가 대량인 경우, 이에 대처하는 방법은? 18-3 Memento 클래스의 변수 number의 액세스 제어 문제
28
연습 문제 18-4 Memento의 인스턴스를 파일로 보존하는 방법
Memento 클래스가 Serializable 인터페이스를 구현하도록 한다. Serializable 인터페이스를 구현한 클래스의 객체는, ObjectOutputStream의 writeObject( )나 ObjectInputStream의 readObject( ) 메소드를 통해서 파일에 쓰고/읽을 수 있다. Memento의 내용을 파일에 쓸 때, 압축 기능을 이용하는 방법 1) import java.util.zip. *; 를 추가한다. 2) 파일에 쓸 때 다음 코드로 수정한다. 3) 파일로부터 읽을 때 다음 코드로 수정한다. ObjectOutput out = new ObjectOutputStream(new DeflaterOutputStream(new FileOutputStream(SAVEFILENAME))); ObjectInput in = new ObjectInputStream(new InflaterOutputStream(new FileInputStream(SAVEFILENAME)));
Similar presentations