[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 |