본문 바로가기

프로그래밍 언어/Architecture

객체 지향 & 패턴 in C (2)

[Contents]

스테이트 패턴

객체지향 스테이트 패턴

 

[출처] '모던 C언어 프로그래밍' - 하나이 시세이


스테이트 패턴

- 상태 변화표를 이용하면, 다이어그램으로 표현하지 못한 빠진 부분 까지 모든 패턴을 확인할 수 있다.

- 상태는 열거형(enum)을 이용해 나타내는 방법이 일반적이다.

- onEvent 함수에서는 전역변수인 state에 따라 event가 다르게 발휘(?) 되도록 한다.

다이어그램
상태 변화표

typedef enum {
    EV_STOP,
    EV_PLAY_PAUSE
} EventCode;

typedef enum {
    ST_IDLE,
    ST_PLAY,
    ST_PAUSE
} State;

void initialize() {
    state = ST_IDLE;
}

void onEvent(EventCode ec) {
    switch(state) {
        case ST_IDLE:
            if(ec == EV_PLAY_PAUSE)
                startPlayer();
            break;

        case ST_PLAY:
            switch(ec) {
                case EV_STOP:
                    stopPlayer();
                    break;
                case EV_PLAY_PAUSE:
                    pausePlayer();
                    break;
                default:
                    break;                
            }
        
        case ST_PAUSE:
            switch(ec) {
                case EV_STOP:
                    stopPlayer();
                    break;
                case EV_PLAY_PAUSE:
                    resumePlayer();
                    break;
                default:
                    break;                
            }

        default:
            break;
    }
}

void stopPlayer() {
    state = ST_IDLE;
}

void pausePlayer() {
    state = ST_PAUSE;
}

void resumePlayer() {
    state = ST_PLAY;
}

void startPlayer() {
    state = ST_PLAY;
}

 

객체지향 스테이트 패턴

- (상태 x 이벤트 수) 경우의 수에 따라 onEvent 함수를 변경하기 보다는 객체 지향을 도입하면 '조건 분기'를 다형적으로 변환할 수 있다.

- State 구조체 안의 stop, playOrPause 함수가 현재 상태에 따라 동작이 달리 된다.

- 두번째 코드는 수행 예시를 나타낸다.

- 장점 : (함수 포인터를 이용한) 다형성을 활용하면 '분기'문이 필요 없어 진다.

- 단점 : 코드 해석이 직관적이지 않다.

//구조체 안의 함수 포인터
// stop, playOrPause 함수가 상태에 따라 동작이 달리 된다 --> 다형성
typedef struct _State {
    const struct _State *(*stop)(const struct _State *pThis);
    const struct _State *(*playOrPause)(const struct _State *This);
} State;

void initialize();
void onStop();
void onPlayOrPause();

static const State *pCurrentState;

//구조체를 반환하는 함수 포인터
static const State *ignore(const State *pThis);
static const State *startPlay(const State *pThis);
static const State *stopPlay(const State *pThis);
static const State *pausePlay(const State *pThis);
static const State *resumePlay(const State *pThis);

const State IDLE = {
    ignore,      // [Stop] 버튼 눌렸을 때의 동작
    startPlay    // [Play/Pause] 버튼 눌렸을 때의 동작
};

const State PLAY = {
    stopPlay,    // [Stop] 버튼 눌렸을 때의 동작
    pausePlay    // [Play/Pause] 버튼 눌렸을 때의 동작
};

const State PAUSE = {
    stopPlay,    // [Stop] 버튼 눌렸을 때의 동작
    resumePlay   // [Play/Pause] 버튼 눌렸을 때의 동작
};

void initialize() {
    pCurrentState = &IDLE;
}

//stop 함수 포인터가 가리키는 함수를 호출한다. 각각의 함수는 새로운 상태를 반환한다.
//onStop()함수에서는 stop 함수 포인터가 어떤 함수를 호출하는지는 관여하지 않는다.
void onStop() {
    pCurrentState = pCurrentState->stop(pCurrentState);
}

//playOrPause 함수 포인터가 가리키는 함수를 호출한다. 각각의 함수는 새로운 상태를 반환한다.
//onPlayOrPause()함수에서는 playOrPause 함수 포인터가 어떤 함수를 호출하는지는 관여하지 않는다.
void onPlayOrPause() {
    pCurrentState = pCurrentState->playOrPause(pCurrentState);
}

static const State *ignore(const State *pThis) {
    return pCurrentState;
}

static const State *stopPlay(const State *pThis) {
    return &IDLE;
}

static const State *pausePlay(const State *pThis) {
    return &PAUSE;
}

static const State *resumePlay(const State *pThis) {
    return &PLAY;
}

static const State *startPlay(const State *pThis) {
    return &PLAY;
}
initilize();     // State 구조체의 stop 함수포인터는 ignore함수를, 
                 // playOrPause 함수 포인터는 startPlay 함수를 가리킨다.

onPlayOrPause(); // 버튼을 누르면 pCurrentState->playOrPause가 수행된다. 
                 // onPlayOrPause() 함수에서는 playOrPause 함수 포인터를 이용해 함수를 호출할 뿐
                 // playOrPause 함수 포인터가 어떤 함수를 호출하는 지는 관여하지 않는다.
                 // 추상화 되어 인터페이스의 역할만 수행한다.
                 
                 // 현재 pCurrentState->playOrPause 함수 포인터는 startPlay 함수를 가리키고 있다.
                 // 따라서, startPlay가 수행되며 pCurrentState = &PLAY가 된다. 
                 // stop 함수포인터는 stopPlay 함수를
                 // playOrPause 함수 포인터는 pausePlay 함수를 가리키게 된다.
                 // 따라서, IDLE -> PLAY

onPlayOrPause(); // 버튼을 누르면 pCurrentState->playOrPause가 수행된다. 
                 // onPlayOrPause() 함수에서는 playOrPause 함수 포인터를 이용해 함수를 호출할 뿐
                 // playOrPause 함수 포인터가 어떤 함수를 호출하는 지는 관여하지 않는다.
                 // 추상화 되어 인터페이스의 역할만 수행한다.
                 
                 // 현재 pCurrentState->playOrPause 함수 포인터는 pausePlay 함수를 가리키고 있다.
                 // 따라서, pausePlay가 수행되며 pCurrentState = &PAUSE가 된다.
                 // stop 함수포인터는 stopPlay 함수를
                 // playOrPause 함수 포인터는 resumePlay 함수를 가리키게 된다.
                 // 따라서, PLAY -> PAUSE

onPlayOrPause(); // 버튼을 누르면 pCurrentState->playOrPause가 수행된다. 
                 // onPlayOrPause() 함수에서는 playOrPause 함수 포인터를 이용해 함수를 호출할 뿐
                 // playOrPause 함수 포인터가 어떤 함수를 호출하는 지는 관여하지 않는다.
                 // 추상화 되어 인터페이스의 역할만 수행한다.
                 
                 // 현재 pCurrentState->playOrPause 함수 포인터는 resumePlay 함수를 가리키고 있다.
                 // 따라서, resumePlay가 수행되며 pCurrentState = &PLAY가 된다.
                 // stop 함수포인터는 stopPlay 함수를
                 // playOrPause 함수 포인터는 pausePlay 함수를 가리키게 된다.
                 // 따라서, PAUSE -> RESUME
                 
onStop();        // 버튼을 누르면 pCurrentState->stop가 수행된다.
                 // onStop() 함수에서는 stop 함수 포인터를 이용해 함수를 호출할 뿐 
                 // stop 함수 포인터가 어떤 함수를 호출하는 지는 관여하지 않는다.
                 // 추상화 되어 인터페이스의 역할만 수행한다.
                 
                 // 현재 pCurrentState->stop 함수 포인터는 stopPlay 함수를 가리키고 있다.
                 // 따라서, stopPlay가 수행되며 pCurrentState = &IDLE가 된다.
                 // stop 함수포인터는 ignore 함수를
                 // playOrPause 함수 포인터는 startPlay 함수를 가리키게 된다.
                 // 따라서, RESUME -> STOP
"동일한 인터페이스에 상태에 따라 동작이 달라지는 SW를 구현해야 한다면 '상태 변화 표'를 먼저 작성하도록 한다"

 

 

템플릿 메서드 패턴

- '자원을 관리하는 코드'가 '자원을 사용하는 코드'를 간싸는 형태의 경우 템플릿 메서드 패턴을 고려해 볼 수 있다.

- 템플릿 메서드 패턴은 프로그램중 일부 처리 내용을 '함수 형태'로 바꿀 수 있게 하여 그 외의 부분(메서드 부분)을 정형화된 형태로 재사용 할 수 있게 해준다. 

 

 


 

반응형

'프로그래밍 언어 > Architecture' 카테고리의 다른 글

객체 지향 & 패턴 in C (1)  (0) 2021.08.17
SOLID (객체 지향 설계) 설명  (0) 2021.04.17