자식 상태 머신이 어떻게 컨트롤을 부모 상태 머신으로 되돌릴 수 있습니까? 이것을 부모 상태

내 최상위 상태 시스템에는 몇 가지 상태와 가장자리가 있습니다. 이것을 부모 상태 머신이라고합니다.

A ----> B ----> C

상위 상태 시스템 내의 모든 상태도 상태 시스템 일 수 있습니다. 이 아이들 상태 머신이라고 부를 것입니다.

           ___________
         /            \
A ----> |  B0->B1->B2  | ----> C
         \____________/

상위 상태 머신이 A에서 B로 전환되면 B의 상태 머신이 대신합니다. B가 실행을 마치면 어떻게 부모 상태 머신에 대한 제어권을 포기하고 상태 C로 전환해야합니까? 어떤 디자인 패턴을 사용하십니까?

궁금한 점이 있다면 정확한 프로젝트가 상당히 복잡하고 자식 상태의 내부 작업을 캡슐화하는 것이 자연 스럽기 때문에 부모 상태 머신 내에 자식 상태 머신이 있습니다.



답변

모든 상태 머신에는 일종의 이벤트 핸들러와 해당 이벤트를 트리거하는 수단이 있습니다. 해당 핸들러는 기존 상태 및 이벤트 유형을 입력으로 사용하고 새 상태를 선택하며 선택적으로 부작용 코드를 실행합니다.

기본적으로 B주 이벤트 핸들러는 이벤트 핸들러로 인식하지 않고 B상태를 유지 하는 이벤트를 전달합니다 B. 때 B전환하고 싶어 C, 그것은 메인 이벤트 핸들러에 적절한 이벤트를 보냅니다.


답변

당신이 읽고 Taoup의이 섹션을 ? 이를 수행하는 여러 가지 방법이 있지만 그 중 다수는 상태 머신을 분할 한 방법에 따라 다릅니다 . 별도의 프로세스입니까? 실? 사물?

그것들을 어떻게 구축했는지 파악하고, 그들이 의사 소통을 할 수있는 표준적인 방법이 있는지 살펴보십시오. 존재하지 않는 경우 시스템을 잘못 설계 한 것일 수 있습니다.

나를 위해 stdin과 stdout을 연결하는 별도의 프로세스를 살펴 보겠습니다. 그런 다음 자식 상태 시스템은 독립형이되어 stdin에서 작동하고 stdout에서 출력합니다. 자식 프로세스를 시작하고 파이프를 연결 한 다음 데이터를 덤프하고 결과를 기다리는 것이 부모 상태 시스템의 작업이됩니다. 이 모든 것들이 이미 모든 현대 언어로 이루어 졌으므로 쉽게 할 수 있습니다.


답변

두 상태 머신을 분리하고 그 사이에 메시지 전달을 사용하십시오. 따라서 상태 머신 1은 ABC에서 진행하며 상태 B에서는 상태 머신 2의 현재 결과를 확인합니다. 출력이 변경되면 상태 머신 1이이를 인식 할 수 있으며 상태 머신 2는 인식 할 필요가 없습니다. 상태 머신 1이 실제로 작동하는 방식 다음과 같은 것 :

typedef struct StateMachine {
  void(*Update)(); // function to update the state machine
  int Data;        // generic temp holder to survive state contexts
  int State;       // current state of our state machine
  int *Message;    // pointer to a shared integer for message passing
};

int main(void) {
  int Message = 0;
  /* NewStateMachine would malloc the struct, pass in the int reference
   * and function pointer as well as add it to a circularly linked list */
  NewStateMachine(&Message, MainLoop);
  NewStateMachine(&Message, MinorLoop);
  StateMachine *Current = StateMachine_CLL.First;

  for(;;) {
    Current->Update(Current); /* Update the current state machine */
    Current = Current->Next;  /* And the advance to the next one */
  }
}

void MainLoop(StateMachine *this) {
  switch(this.State) {
  case 0:
    CloseCoolantTank(1); /* safe to call if valve already closed */
    CloseCoolantTank(2); /* safe to call if valve already closed */
    this.State = 1;
    break;
  case 1:
    /* we have a message, do something */
    if(*this.Message) this.State = 2;
    /* otherwise stall at this state until we get a message */
    else this.State = 1;
    break;
  case 2:
    if(*this.Message == 1) this.State = 3;      /* warm */
    else if(*this.Message == 2) this.State = 4; /* hot! */
    else this.State = 0;                        /* cooled down, shut off valves */
    this.Message = 0;                           /* clear the message */
    break;
  case 3:
    OpenCoolantTank(1); /* opens the valve, safe to call if already open */
    this.State = 2;     /* recheck for new message */
    break;
  case 4:
    OpenCoolantTank(2); /* opens the valve, safe to call if already open */
    this.State = 3;     /* also open coolant tank 1 for extra cooling */
    break;
  }
}

/* Monitor temperature and send messages on overheat */
void MinorLoop(StateMachine *this) {
  switch(this.State) {
  case 0:
    this.Data = ReadADCValue();
    this.State = 1;
    break;
  case 1:
    if(this.Data > 150) *this.Message = 2;
    else if(this.Data > 100) *this.Message = 1;
    this.State = 0;
    break;
  }
}

답변

해결책은 1) A의 하위 상태가 B의 하위 상태에 가시적인지 여부에 달려 있습니다. 2) AB와 C는 공통 부모로부터 파생됩니다. 공통 부모가 있고 가시성이 보편적이라면 B의 하위 상태에서 A의 하위 상태로 이동하는 데 너무 많은 문제가 없어야합니다.

네임 스페이스 및 / 또는 A, B 및 C를 통해 네임 스페이스를 분리하지 않은 경우 공통 상위가없는 경우 A, B 및 C 머신에 대한 외부 상태 변경 드라이버를 사용하는 것이 가장 좋습니다. 이벤트 핸들러를 통해 수행 할 수 있습니다. B에 발생한 이벤트를 수신하고 이벤트를 기반으로 자체 하위 상태로 전환 할 수있는 A에 관찰자를두기 만하면됩니다.