Qual è lo schema di progettazione alternativo di una macchina a stati che può avere una modifica dello stato ricorsiva?

3

Ho un codice a macchina di stato, che ha stato A, B e C, con una funzione changeState (char i) che può cambiare lo stato di un sistema, e ogni stato può anche chiamare changeState (char i) per passare il sistema in un altro stato:

struct State{
    State(char aName){
        this->name=aName;
    }
    char name;
};

void changeState(char i);

struct A : public State{
    A():State('A'){
        changeState('B');
    }
};

struct B : public State{
    B():State('B'){
    changeState('C');
    }
};

struct C : public State{
    C():State('C'){
    }
};

State* state;
void changeState(char i){
    switch(i){
        case 'A':
            state=new A();
            break;
        case 'B':
            state=new B();
            break;
        case 'C':
            state=new C();
            break;
    }
}

Ora questo codice ha almeno 2 problemi:

  1. se non imposto lo stato come globale, devo gestire la gestione della memoria di ogni stato, il che aumenta la complessità del codice

  2. se si verifica un cambiamento dello stato ricorsivo, lo stato finale non è quello che mi aspettavo:

Supponiamo di voler attivare la modifica dello stato, iniziare da A a C (per semplificare il codice ora il cambiamento di stato si verifica solo nel costruttore, ma nel codice reale è in un altro metodo che chiama dopo il costruttore):

int main(){
    changeState('A');
    printf("%c\n",state->name);
    return 0;
}

il flusso è: changeState ('A') - > new A () - > changeState ('B') - > new B () - > changeState (C) - > new C () , Voglio che l'ultimo stato sia C, ma ora è A.

La mia domanda è: qual è l'architettura software alternativa o il modello di progettazione se la mia macchina a stati potrebbe cambiare lo stato in modo ricorsivo? o, se possibile, come modificare il codice corrente per superare il problema?

    
posta ggrr 22.12.2015 - 03:10
fonte

1 risposta

2

Prima di tutto, eviterei sempre lo stato globale a meno che non sia assolutamente necessario. Rende difficile estendere il tuo codice, testare il tuo codice e eseguire il debug del tuo codice. Per la spesa di avere un oggetto macchina di stato che viene passato a ogni stato quando viene creato? Sicuramente no.

Ma non penso sia comunque necessario. Il tuo codice ha un altro antipattern che non hai evidenziato: i tuoi oggetti State causano effetti collaterali nei loro costruttori, che è una seria idea. Risolverei entrambi i problemi con una singola modifica: ho una classe StateMachine con un metodo:

void changeState(State * requested)
{
    State * alternative;
    while (alternative = requested-> getAlternative ())
        requested = alternative;
    currentState = requested;
}

Dove getAlternative è un metodo virtuale in stato che restituisce null e può essere sovrascritto da ciascuna sottoclasse per restituire uno stato diverso se necessario.

Una volta fatto questo, puoi allocare una singola istanza di ciascuna sottoclasse di stato e usare quelle invece di chiamare "nuova" tutto il tempo, il che salverà la possibilità di perdite di memoria (che potrebbero sembrare un problema in il tuo design).

    
risposta data 22.12.2015 - 14:01
fonte

Leggi altre domande sui tag