728x90

프로그램을 작성하다 보면 객체를 이전 상태로 되돌려야 하는 경우가 있습니다. 만약에 바둑판이라는 객체가 있을 때 연습모드나 복기를 위해서 상태를 뒤로 돌리는 기능이 필요할 수 있습니다.

 

바둑판 객체의 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 클래스 내부의 정보를 자신의 상태 정보로 사용하는 클래스를 위함이고 후자는 당연히 일반적인 클래스를 위함입니다.

 

 

728x90

'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

+ Recent posts