I have to implement a status transition validation, like:
'P' -> 'W' -> 'M' -> 'F' -> 'G'
'P' -> 'I' -> 'B' -> 'M' -> 'F' -> 'G'
where "->"
denotes "can move to"
In short, a particular status can move to only certain status, but not other status. As in above example, P can move to W or I but not any other status.
Note: There are finite number of status in the system.
I have read about Strategy pattern, but I did not feel this particular problem suited to it. What will be best way to implement this in Java 8 ?
My suggestion would be to write an enum State
that represents each available state and the valid transitions:
enum State {
G(Collections.emptySet()),
F(Collections.singleton(G)),
M(Collections.singleton(F)),
B(Collections.singleton(M)),
I(Collections.singleton(B)),
W(Collections.singleton(M)),
P(new HashSet<>(Arrays.asList(W, I)));
private final Set<State> validTransitions;
State(final Set<State> validTransitions) {
this.validTransitions = validTransitions;
}
public State transitionTo(final State next) {
if(!validTransitions.contains(next)) {
throw new IllegalStateException();
}
return next;
}
}
NB:
This will only allow for a DAG of state transitions, if you reference a State
in validTransitions
that hasn't yet been declared then you will get an "Illegal forward reference" compiler error.
I see this as an advantage as it will enforce a valid set of states at compile time, but only if your states are acyclic.
If you are using Java 9+
enum State {
G(Collections.emptySet()),
F(Set.of(G)),
M(Set.of(F)),
B(Set.of(M)),
I(Set.of(B)),
W(Set.of(M)),
P(Set.of(W, I));
private final Set<State> validTransitions;
State(final Set<State> validTransitions) {
this.validTransitions = validTransitions;
}
public State transitionTo(final State next) {
if(!validTransitions.contains(next)) {
throw new IllegalStateException();
}
return next;
}
}
There is the state pattern, and of course, you could also think of implementing a complete state machine yourself.
You specify within code which events allow specific state transitions.
In other words: you don't think in terms of "black listing" (P can't go to B), but instead you explicitly state (P can go to W and I, period).
For further reading, see here for a discussion of state pattern vs state machine.
I have had a requirement like this. This is how I solved it. The idea is to maintain an enum and process the next states only if they are valid.
public enum State {
P {
@Override
public Boolean isValid(State nextState) {
return nextState==State.W || nextState==State.I;
}
},
W {
@Override
public Boolean isValid(State nextState) {
return nextState==State.M;
}
},
M {
@Override
public Boolean isValid(State nextState) {
return nextState==State.F;
}
},
I {
@Override
public Boolean isValid(State nextState) {
return nextState==State.B;
}
},
B {
@Override
public Boolean isValid(State nextState) {
return prevState==State.M;
}
},
G {
@Override
public Boolean isValid(State nextState) {
return false;
}
},
F {
@Override
public Boolean isValid(State nextState) {
return nextState==State.G;
}
};
public abstract Boolean isValid(State nextState);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With