Elegant State Machines: A Java Enum Approach

This article presents an innovative approach to implementing State Machines in Java using Enums instead of traditional interface-based implementations. The technique reduces boilerplate code, simplifies persistence with Hibernate, and maintains better encapsulation of state-dependent behavior. Through practical code examples, it demonstrates how to create a three-state machine with transition logic, while adhering to Command Query Separation (CQS) principles and improving overall testability. The approach offers a more natural fit for state representation compared to conventional interface/implementation patterns.

2 Minutes reading time

Behold the masterpiece that AI hallucinated while reading this post:

"The Little State Machine That Could: How Jimmy The Java Enum Found His Perfect Role"

(after I fed it way too many marketing blogs and memes)

Created using DALL-E 3

AI-Generated: The Little State Machine That Could: How Jimmy The Java Enum Found His Perfect Role

Often we need to implement State Machines to encapsulate object behavior depending on a given object state. This can be cumbersome as it often leads to a lot of interfaces and implementation classes, and persisting such an object state often leads to problems, as we would need to implement a Hibernate custom type for instance to solve it.

Today i want you to show another approach. We model a State Machine using a Java Enum. The Enum concept fits better to the State Machine concept, and Java Enums are much more like an enumeration, they can also contain behavior!

This is a simple State Machine implemented using plain Java Enums:

package de.mirkosertic;

public enum State {

    INITIAL {
        @Override
        State doSomething(String aParameter) {
            System.out.println("Doing Something in INITIAL state and jumping to NEXT_STEP, argument = " + aParameter);
            return NEXT_STEP;
        }
    },
    NEXT_STEP {
        @Override
        State doSomething(String aParameter) {
            System.out.println("Doing Something in NEXT_STEP and jumping into FINAL, argument = " + aParameter);
            return FINAL;
        }
    },
    FINAL {
        @Override
        State doSomething(String aParameter) {
            System.out.println("I am in FINAL state, argument = " + aParameter);
            return this;
        }
    };

    abstract State doSomething(String aParameter);
}

The State Machine has three states, INITIAL, NEXT_STEP and FINAL, and every state has an Implementation of the doSomething() method. which takes an argument and depending on the argument it can jump to the next state.

This is a small piece of code to show the usage of such a State Machine:

package de.mirkosertic;

public class StatefulObject {

    private State state;

    public StatefulObject() {
        state = State.INITIAL;
    }

    public void performRequest(String aParameter) {
        state = state.doSomething(aParameter);
    }

    public static void main(String[] args) {
        StatefulObject theObject = new StatefulObject();
        theObject.performRequest("Hello");
        theObject.performRequest("Hello");
        theObject.performRequest("Hello");
        theObject.performRequest("Hello");
        theObject.performRequest("Hello");
    }
}

Using Enums to model State Machines fits better than Interface/Implementation classes, and also an Enum can be easily persisted using Hibernate Enum types. Also, we have implemented the CQS principle and increased test-ability a lot.

Stay tuned! If you want to see more of such cool guidelines, please consult Vaughn Vernon excellent book Implementing Domain-Driven Design.

Git revision: 2e692ad