프로그램을 작성하다 보면 객체를 이전 상태로 되돌려야 하는 경우가 있습니다. 만약에 바둑판이라는 객체가 있을 때 연습모드나 복기를 위해서 상태를 뒤로 돌리는 기능이 필요할 수 있습니다.
바둑판 객체의 19x19의 이차원 배열을 통해 해당 위치에 돌이 놓여있는지 아닌지를 판단할 수 있습니다. 흰 돌을 놓았을 때는 음수로 흑 돌을 놓았을 때는 양수로 하여 턴마다 숫자를 하나씩 늘려가며 위치에 몇 번째로 돌을 놓았는지를 판단할 수 있습니다. 그런데 바둑을 하게 되면 돌이 잡히기도 하기 때문에 이러한 정보만으로는 완전히 뒤로 돌린다고는 볼 수 없습니다.
완전한 돌리기를 하기 위해서는 객체가 가지고 있는 객체 전부를 되돌려야 완전히 복구했다고 볼 수 있습니다. 이를 위해서 객체가 가지고 있는 모든 데이터 멤버를 하나의 클래스로 정의하고 그 클래스의 리스트를 객체에서 가지고 있게 된다면 각 액션, 흐름마다 리스트에 추가하여 관리한다면 제대로 되돌리기를 할 수 있게 됩니다.
위처럼 상태 정보를 객체 형태로 저장해서 리스트 자료구조에 의해 필요한 시점에서 특정 시점의 객체의 상태 복원을 쉽게 할 수 있도록 만든 클래스 구조를 메멘토 패턴이라고 합니다.
class GoMemento {
friend class GoBoard;
GoMemento() {
// 바둑판 초기화
}
GoMemento(const GoMemento& rhs) { CopyBoard(rhs); }
GoMemento& operator=(const GoMemento& rhs) { CopyBoard(rhs); }
protected:
void CopyBoard(const GoMemento& src) {
// 데이터 멤버들 복사
}
private:
int board[19][19];
};
class Goboard {
void putStone(int x, int y) {
if ( pCurBoard->board[x][y] == 0 ) {
GoMemento* pNewBoardMemento = new GoMemento(*pCurBoard);
...;
historyList.push_front(pCurBoard);
pCurBoard = pNewBoard;
}
}
void Redo(int cnt) {
if ( cnt <= 0 ) { return ; }
for ( int i=0 ; i<cnt-1 ; i++ ) {
GoMemento* pTmp = historyList.front();
delete pTmp;
historyList.pop_front();
}
delete pCurBoard;
if ( historyList.empty()) { pCurBoard = new GoMemento(); }
else { pCurBoard = historyList.front(); }
}
private:
list<GoMemento*> historyList; // 이전 상태들 저장
GoMemento* pCurBoard; // 현재 상태 저장
};
int main()
{
GoBoard board;
board.putStone(3,3);
board.putStone(1,16);
board.putStone(4,16);
board.putStone(6,4);
board.putStone(4,2);
board.putStone(1,5);
board.Redo(3);
}
GoMemento 클래스는 Goboard 클래스를 friend로 선언하고 있습니다. GoMemento 클래스의 정보는 원래 객체의 정보로 만들어지기 때문에 Goboard 클래스가 GoMemento 클래스 내의 데이터 멤버에 접근할 수 있도록 만들어줍니다.
위와 같이 friend로 선언한다는 것은 GoBoard 클래스에게 GoMemento의 모든 정보를 공개한다는 뜻입니다. 이렇게 선언해주게 되면 GoBoard에서는 GoMemento의 private 멤버까지 접근할 수 있게 됩니다.
Memento 패턴을 사용할 때 Memento 클래스는 클래스 내부의 정보를 모두 접근하도록 폭 넓게 공개된 인터페이스와 규정된 인터페이스를 통해서만 Memento 클래스 객체에 접근할 수 있도록 하는 것이 좋습니다. 전자는 Memento 클래스 내부의 정보를 자신의 상태 정보로 사용하는 클래스를 위함이고 후자는 당연히 일반적인 클래스를 위함입니다.
'Programming > C++' 카테고리의 다른 글
C++ 상태 패턴 (0) | 2021.06.13 |
---|---|
C++ 옵저버 패턴 (0) | 2021.06.13 |
C++ 중재자 패턴 (0) | 2021.06.06 |
C++ 이터레이터 패턴 (0) | 2021.06.06 |
C++ 인터프리터 패턴 (0) | 2021.06.06 |