Implementazione di macchine a stati semplici con enumerazioni Java

1. Panoramica

In questo tutorial, daremo uno sguardo alle macchine a stati e come possono essere implementate in Java usando Enums.

Spiegheremo anche i vantaggi di questa implementazione rispetto all'utilizzo di un'interfaccia e di una classe concreta per ogni stato.

2. Java Enums

Un Java Enum è un tipo speciale di classe che definisce un elenco di costanti. Ciò consente un'implementazione indipendente dai tipi e un codice più leggibile .

Ad esempio, supponiamo di avere un sistema software per le risorse umane in grado di approvare le richieste di ferie presentate dai dipendenti. Questa richiesta viene esaminata dal Team Leader, che la inoltra al Department Manager. Il Responsabile di Dipartimento è il responsabile dell'approvazione della richiesta.

L'enumerazione più semplice che contiene gli stati di una richiesta di ferie è:

public enum LeaveRequestState { Submitted, Escalated, Approved }

Possiamo fare riferimento alle costanti di questa enumerazione:

LeaveRequestState state = LeaveRequestState.Submitted;

Le enumerazioni possono anche contenere metodi. Possiamo scrivere un metodo astratto in un enum, che costringerà ogni istanza di enum a implementare questo metodo. Questo è molto importante per l'implementazione delle macchine a stati, come vedremo di seguito.

Poiché le enumerazioni Java estendono implicitamente la classe java.lang.Enum , non possono estendere un'altra classe. Tuttavia, possono implementare un'interfaccia, proprio come qualsiasi altra classe.

Ecco un esempio di enumerazione contenente un metodo astratto:

public enum LeaveRequestState { Submitted { @Override public String responsiblePerson() { return "Employee"; } }, Escalated { @Override public String responsiblePerson() { return "Team Leader"; } }, Approved { @Override public String responsiblePerson() { return "Department Manager"; } }; public abstract String responsiblePerson(); }

Notare l'utilizzo del punto e virgola alla fine dell'ultima costante enum. Il punto e virgola è richiesto quando abbiamo uno o più metodi che seguono le costanti.

In questo caso, abbiamo esteso il primo esempio con un metodo responsabilePerson () . Questo ci dice la persona responsabile dell'esecuzione di ciascuna azione. Quindi, se proviamo a controllare la persona responsabile per lo stato Escalated , ci darà "Team Leader":

LeaveRequestState state = LeaveRequestState.Escalated; assertEquals("Team Leader", state.responsiblePerson());

Allo stesso modo, se controlliamo chi è responsabile dell'approvazione della richiesta, ci darà "Responsabile di reparto":

LeaveRequestState state = LeaveRequestState.Approved; assertEquals("Department Manager", state.responsiblePerson());

3. Macchine a stati

Una macchina a stati, chiamata anche macchina a stati finiti o automa finito, è un modello computazionale utilizzato per costruire una macchina astratta. Queste macchine possono essere solo in uno stato alla volta. Ogni stato è uno stato del sistema che cambia in un altro stato. Questi cambiamenti di stato sono chiamati transizioni.

Può complicarsi in matematica con diagrammi e notazioni, ma le cose sono molto più facili per noi programmatori.

Lo State Pattern è uno dei ben noti ventitré design pattern del GoF. Questo modello prende in prestito il concetto dal modello in matematica. Consente a un oggetto di incapsulare comportamenti diversi per lo stesso oggetto, in base al suo stato. Possiamo programmare la transizione tra stati e successivamente definire stati separati.

Per spiegare meglio il concetto, amplieremo il nostro esempio di richiesta di permesso per implementare una macchina a stati.

4. Enum come macchine a stati

Ci concentreremo sull'implementazione enum delle macchine a stati in Java. Sono possibili altre implementazioni e le confronteremo nella sezione successiva.

Il punto principale dell'implementazione della macchina a stati usando un enum è che non dobbiamo occuparci dell'impostazione esplicita degli stati . Invece, possiamo solo fornire la logica su come passare da uno stato a quello successivo. Immergiamoci subito in:

public enum LeaveRequestState { Submitted { @Override public LeaveRequestState nextState() { return Escalated; } @Override public String responsiblePerson() { return "Employee"; } }, Escalated { @Override public LeaveRequestState nextState() { return Approved; } @Override public String responsiblePerson() { return "Team Leader"; } }, Approved { @Override public LeaveRequestState nextState() { return this; } @Override public String responsiblePerson() { return "Department Manager"; } }; public abstract LeaveRequestState nextState(); public abstract String responsiblePerson(); }

In questo esempio, le transizioni della macchina a stati vengono implementate utilizzando i metodi astratti di enum . Più precisamente, usando nextState () su ogni costante enum, specifichiamo la transizione allo stato successivo. Se necessario, possiamo anche implementare un metodo previousState () .

Di seguito è riportato un test per verificare la nostra implementazione:

LeaveRequestState state = LeaveRequestState.Submitted; state = state.nextState(); assertEquals(LeaveRequestState.Escalated, state); state = state.nextState(); assertEquals(LeaveRequestState.Approved, state); state = state.nextState(); assertEquals(LeaveRequestState.Approved, state);

Iniziamo la richiesta di ferie nello stato iniziale Inviato . Verifichiamo quindi le transizioni di stato utilizzando il metodo nextState () che abbiamo implementato sopra.

Nota che poiché Approvato è lo stato finale, non può avvenire nessun'altra transizione .

5. Vantaggi dell'implementazione di macchine a stati con Java Enums

L'implementazione di macchine a stati con interfacce e classi di implementazione può essere una quantità significativa di codice da sviluppare e mantenere.

Poiché un'enumerazione Java è, nella sua forma più semplice, un elenco di costanti, possiamo usare un'enumerazione per definire i nostri stati. E poiché un enum può contenere anche un comportamento, possiamo usare metodi per fornire l'implementazione della transizione tra gli stati.

Avere tutta la logica in una semplice enumerazione consente una soluzione pulita e diretta.

6. Conclusione

In questo articolo, abbiamo esaminato le macchine a stati e come possono essere implementate in Java utilizzando Enums. Abbiamo dato un esempio e lo abbiamo testato.

Alla fine, abbiamo anche discusso i vantaggi dell'utilizzo di enumerazioni per implementare macchine a stati. In alternativa all'interfaccia e alla soluzione di implementazione, le enumerazioni forniscono un'implementazione più pulita e più facile da comprendere delle macchine a stati.

Come sempre, tutti gli snippet di codice menzionati in questo articolo possono essere trovati nel nostro repository GitHub.