lundi 26 juin 2023

How to elegantly define shortest happy path between start and end state in a State Pattern design implementation?

When using state pattern, one is delegating behaviour of the Entity to the current State. I have prepared SSCCE that I pasted below. We have an Entity which can transition between states : StartState, StateA, StateB, EndState. The happy path is simply the shortest path between StartState and EndState.

In this particual example the shortest happy path is: StartState->StateA->StateB->EndState

I find it hard to express what is the happy path, so I can return the order to the frontend but also be able to navigate myself through. State pattern uses methods not dicitonaries to store nodes end edges, right? I want to avoid just List<string>. I'm looking for an elegant solution.

I want to be able to know upfront what is the order of the states in the happy path.

  1. Dynamically calculate the shortest path for that class structure? XOR
  2. Because dynamic calculation is probably hard or very hacky, what is the most elastic/convienient to use way to describe statically the shortest happy path in this state graph?

namespace States
{
    public class Entity
    {
        public int Id { get; private set; }
        public State CurrentState { get; private set; }
        public void Action1() => CurrentState = CurrentState.Action1() ?? CurrentState;
        public void Action2() => CurrentState = CurrentState.Action2() ?? CurrentState;
        public void Action3() => CurrentState = CurrentState.Action3() ?? CurrentState;
    }

    public abstract class State
    {
        protected Entity _entity;

        public State(Entity entity)
        {
            _entity = entity;
        }
        //They return new state or null if transition is illegal
        public abstract State Action1();
        public abstract State Action2();
        public abstract State Action3();
    }

    public class StartState : State
    {
        public StartState(Entity entity) : base(entity) { }
        public override State Action1() => new StateA(_entity); //moves workflow forward
        public override State Action2() => null;
        public override State Action3() => null;
    }

    public class StateA : State
    {
        public StateA(Entity entity) : base(entity) { }
        public override State Action1() => null;
        public override State Action2() => new StateB(_entity); //moves workflow forward
        public override State Action3() => new StartState(_entity); // goes back to StartState
    }

    public class StateB : State
    {
        public StateB(Entity entity) : base(entity) { }
        public override State Action1() => new EndState(_entity); //moves workflow forward to EndState
        public override State Action2() => null;
        public override State Action3() => new StartState(_entity); // goes back to StartState
    }

    public class EndState : State
    {
        public EndState(Entity entity) : base(entity) { }
        public override State Action1() => null;
        public override State Action2() => null;
        public override State Action3() => null;
    }
}

Aucun commentaire:

Enregistrer un commentaire