Progettazione menu macchina a stati finiti

7

Sto creando un FSM in python (è un sequenziatore di passi e un sample pad basato su un Raspberry Pi 2).

In questo momento ci sono due stati e il terzo è il Menu. Questo è gestito da un sistema di classe che gestisce tutti gli stati. Lo stato del menu deve modificare gli attributi degli altri stati, quindi ho passato gli altri stati al suo costruttore.

    class State(object):
        def buttonPress(self, numberButton):
            raise NotImplementedError

    class StepSequencer(State):
        def buttonPress(self, numberButton):
            ...

    class SamplePad(State):
        def buttonPress(self, numberButton):
            ...

    class Menu(State):
        def __init__(self,stepSequencer,samplePad):
            self.stepsequencer = stepSequencer
            self.samplepad = samplePad
        def buttonPress(self, numberButton):
            ...
        def setMenuItem(self, currentMenuItem):
            self.currentMenuItem = currentMenuItem

    class MenuItem(object):
        def __init__(self, text):
            self.text = text

    class System(object):
        def __init__(self):
            self.stepsequencer = StepSequencer()
            self.samplepad = SamplePad()
            self.menu = Menu(self.stepsequencer, self.samplepad)
        def setState(self,state):
            self.state = state
        def buttonPress(self, numberButton):
            self.state.buttonPress(numberButton)

Non riesco a capire come creare la struttura per il menu. Ho pensato di creare una classe MenuItem per ogni voce di menu in modo che lo stato Menu contenga tutti questi oggetti e possa modificare il MenuItem corrente, tuttavia, ci sono alcune cose che non posso superare:

  • come posso dichiarare tutte le voci di menu e passarle nello stato Menu per creare dinamicamente la struttura del menu?

  • con questo menu:

    Step Sequencer settings:
        Set samples
        Set volume
    Sample Pad setting:
        Set samples
        Set volume
    

    per esempio, se voglio avere un cursore che imposta il volume, devo creare un altro stato per gestirlo? Questo stato può essere un altro stato del sistema o deve essere un sottostato dello stato del menu?

  • posso eseguire il codice di ogni stato usando questa logica? :

    while(system.state = system.stepsequencer):
        ...
    while(system.state = system.menu):
        ...
    

    Lo stato viene modificato da un listener in un'altra discussione. Questo sembra un modo molto non ortodosso di gestire gli stati, ma sembra funzionare. Se questo è efficace, come posso gestire i sottostati di Menu?

posta user3466127 28.01.2016 - 17:15
fonte

1 risposta

1

Probabilmente il modo migliore per implementare gli FSM nelle lingue OOP è attenersi al Pattern di progettazione dello stato

Questo modello fornisce una struttura generale per progettare e implementare gli SFM, dividendo l'FSM in due "lati": il contesto e lo stato, essendo lo stato una gerarchia di tutti i diversi stati che il contesto può avere (un contesto può avere solo un numero molto limitato di stati in una sola volta, di solito solo uno). Quindi, le transizioni di stato sono modellate, di solito nel client (la classe che usa il contesto deciderà quando passare esplicitamente a un nuovo stato, facendo in modo che l'istanza di contesto si comporti diversamente), o negli stati (quando ogni stato è responsabile di verificare le condizioni di transizione ed eseguire la transizione effettiva). Il primo approccio ha il vantaggio di una minore coesione tra stati, rendendoli riutilizzabili nelle implementazioni di altri FSM, ma con una classe cliente molto più complessa. Il secondo approccio ha gli effetti collaterali opposti.

Nel tuo caso, modellerei System con il ruolo di Context, State con il ruolo di State e Menu (e i suoi sottomenu) come ConcreteStates differenti. Se StepSequencer e SamplePad devono essere esclusivi di un singolo menu / sottomenu, sarebbero attributi di quello stato individuale. Altrimenti, (cioè, condiviso o persistente tra i menu), come attributi di Sistema. Questa decisione dipenderà dalle tue reali esigenze.

Ora, riguardo al tuo design (e questo è un po 'fuori tema), StepSequencer e SamplePad non sembrano stati reali (né per il loro uso né per il loro nome). Personalmente, scommetto che è così solo perché la loro interfaccia è apparentemente identica a quella degli stati ( def buttonPress(self, numberButton): ). Se questo è il motivo, e tu l'hai fatto in questo modo per evitare di duplicare il codice, non farlo. Va bene avere il codice duplicato in determinate occasioni (ad esempio, quando l'interfaccia di base è suscettibile di cambiare in diverse direzioni nel tempo, come affermato da Robert C. Martin nel suo libro Clean Architecture). Tieni questo a mente e considera di derivarli da una diversa classe base diversa da State.

    
risposta data 18.02.2018 - 02:59
fonte