dimanche 18 avril 2021

State pattern in Spring with annotations

I'm trying to refactor a switch-case based state transition in the current codebase. It's painful to add more states now(already has 10+), there are multiple places with if-else/switch cases that check the state before deciding on some action.

The idea is to use state pattern with spring, with states represented as singleton spring beans, and a context that represents current transformation.

Each state has a set of transformation operations performed before it is reached, this is represented within the state interface

public interface State {
    StateContext transformFrom(StateContext stateContext);
}

The state context represents current state and the business entity on which the state transformation is performed upon

public class StateContext {
    private State currentState;
    private PersistentBusinessEntity businessEntity;
}

Individual state beans can either be created seperatly, or in a common bean config

@Configuration
public class StateBeanConfiguration {

    @Bean("STATE_ONE")
    public State transitionToStateOne(){
        return (StateContext context)->{
            //do state transformation
        };
    }

    @Bean("STATE_TWO")
    public State transitionToStateTwo(){
        return (StateContext context)->{
            //do state transformation
        };
    }
}

The state beans are mapped into a state manager, which does the actual work of mapping between states

public class StateManager {
    // Auto-injected state map
    Map<String, State> stateMap;

    public State getStateFromName(String stateName){
        return stateMap.get(stateName);
    }
    public StateContext transitionState(StateContext stateContext, State targetState){
        targetState.transformFrom(stateContext);
        stateContext.setCurrentState(targetState);
        return stateContext;
    }
}

Now to transform state, can just request the manager to map from current state to target state.

public void updateStateForBusinessObject(PersistentBusinessEntity entity,
         StateChangeRequest stateChangeRequest){
    State currentState = stateManager.getStateFromName(entity.currentState);
    StateContext stateContext = new StateContext(currentState, entity);
    State targetState = stateManager.getStateFromName(stateChangeRequest.stateChangeTo);
    stateManager.transitionState(stateContext, targetState);
}

Reducing code for brevity, but the actual implementation has some more caveats including chaining state transitions. The updateStateForBusinessObject method is to be invoked on http client request.

Are there any issues with spring on designing states this way, specifically with creating context on every request and referencing singleton bean objects. I have read elsewhere that states objects should be created for each transformation request, but that seems to be a waste since the actual state beans don't hold request-state.

How can I improve upon this?

Aucun commentaire:

Enregistrer un commentaire