[C++] 유한 상태 기계(Finite State Machine) 모델 접근 방법
유한 상태 기계(FSM)란?
유한 상태 기계(FSM)는 유한한 개수의 상태(state)와 상태 간의 전이(transition)로 시스템의 동작을 모델링하는 수학적 계산 모델입니다. 시스템은 항상 유한한 상태 중 하나에 있으며, 특정 입력이나 이벤트가 발생하면 다른 상태로 전이합니다.
마치 신호등이 빨간색, 초록색, 노란색 상태를 반복하며 바뀌는 것처럼, FSM은 시스템의 가능한 모든 상태와 상태 변화 규칙을 명확하게 정의합니다.
디자인 패턴이라기보다는 프로그래밍에서 널리 사용되는 모델 또는 접근 방식이기 때문에 상태 패턴이 FSM을 객체 지향적으로 구현하는 한 가지 방법이라고 생각해 주시면 됩니다.
유한 상태 기계(FSM)의 구성 요소
- 상태 (State): 시스템이 가질 수 있는 고유한 상황 또는 모드입니다. 예를 들어, “대기”, “걷기”, “공격”, “점프” 등이 상태가 될 수 있습니다.
- 전이 (Transition): 한 상태에서 다른 상태로의 변화입니다. 전이는 특정 입력 또는 이벤트에 의해 발생합니다. 예를 들어, “점프 버튼 누름”이라는 입력이 발생하면 “걷기” 상태에서 “점프” 상태로 전이할 수 있습니다.
- 입력 (Input) 또는 이벤트 (Event): 상태 전이를 유발하는 외부 자극입니다. 예를 들어, 키보드 입력, 마우스 클릭, 시간 경과 등이 입력 또는 이벤트가 될 수 있습니다.
- 시작 상태 (Initial State): 시스템이 처음 시작할 때의 상태입니다.
- 종료 상태 (Final State) (선택적): 시스템의 동작이 종료되는 상태입니다. 모든 FSM이 종료 상태를 가져야 하는 것은 아닙니다.
유한 상태 기계(FSM)의 작동 방식
- 시스템은 시작 상태에서 시작합니다.
- 입력 또는 이벤트가 발생하면 현재 상태와 입력에 따라 정의된 전이 규칙을 확인합니다.
- 전이 규칙에 따라 다음 상태로 전이합니다.
- 새로운 상태에서 시스템은 해당 상태에 정의된 동작을 수행합니다.
- 위의 과정을 반복합니다.
유한 상태 기계(FSM)의 장단점
장점
- 시스템 동작의 명확한 모델링: 시스템의 가능한 모든 상태와 상태 변화 규칙을 명확하게 정의할 수 있습니다.
- 설계 및 구현의 용이성: 복잡한 시스템의 동작을 단순한 상태와 전이로 나누어 설계하고 구현할 수 있습니다.
- 검증 및 테스트의 용이성: FSM을 사용하여 시스템의 동작을 체계적으로 검증하고 테스트할 수 있습니다.
단점
- 상태가 많아질 경우 복잡도가 증가할 수 있습니다.
- 상태 간의 복잡한 상호 작용을 표현하기 어려울 수 있습니다.
FSM의 표현 방법
- 상태 다이어그램 (State Diagram): 상태를 원으로, 전이를 화살표로 나타내는 그래프 형태의 표현 방법입니다. 시각적으로 FSM의 동작을 쉽게 이해할 수 있도록 도와줍니다.
- 상태 전이 테이블 (State Transition Table): 상태와 입력을 행과 열로 나열하고, 각 셀에 다음 상태를 나타내는 표 형태의 표현 방법입니다. FSM의 동작을 명확하게 정의할 수 있습니다.
활용 예시
- 게임 AI: 캐릭터의 행동 패턴 (대기, 이동, 공격, 방어 등)을 FSM으로 구현합니다.
- 컴파일러: 프로그래밍 언어의 구문 분석 과정을 FSM으로 모델링합니다.
- 네트워크 프로토콜: 네트워크 장비의 상태 변화를 FSM으로 정의합니다.
- 자동 판매기, 신호등 등 상태 변화가 명확한 시스템.
예시코드
#include <iostream>
enum class DoorState {
CLOSED,
OPEN,
OPENING,
CLOSING
};
enum class DoorEvent {
OPEN_BUTTON_PRESSED,
CLOSE_BUTTON_PRESSED,
FULLY_OPENED,
FULLY_CLOSED
};
void HandleEvent(DoorState& currentState, DoorEvent event) {
switch (currentState) {
case DoorState::CLOSED:
if (event == DoorEvent::OPEN_BUTTON_PRESSED) {
std::cout << "Opening the door..." << std::endl;
currentState = DoorState::OPENING;
}
break;
case DoorState::OPEN:
if (event == DoorEvent::CLOSE_BUTTON_PRESSED) {
std::cout << "Closing the door..." << std::endl;
currentState = DoorState::CLOSING;
}
break;
case DoorState::OPENING:
if (event == DoorEvent::FULLY_OPENED) {
std::cout << "Door is fully opened." << std::endl;
currentState = DoorState::OPEN;
}
break;
case DoorState::CLOSING:
if (event == DoorEvent::FULLY_CLOSED) {
std::cout << "Door is fully closed." << std::endl;
currentState = DoorState::CLOSED;
}
break;
}
}
int main() {
DoorState currentState = DoorState::CLOSED;
HandleEvent(currentState, DoorEvent::OPEN_BUTTON_PRESSED);
HandleEvent(currentState, DoorEvent::FULLY_OPENED);
HandleEvent(currentState, DoorEvent::CLOSE_BUTTON_PRESSED);
HandleEvent(currentState, DoorEvent::FULLY_CLOSED);
return 0;
}