Skip to content

Instantly share code, notes, and snippets.

@gabrielbauman
Created July 10, 2019 21:49
Show Gist options
  • Save gabrielbauman/42ac75757146af9e864f334221f2be06 to your computer and use it in GitHub Desktop.
Save gabrielbauman/42ac75757146af9e864f334221f2be06 to your computer and use it in GitHub Desktop.
A very simple (but useful) method of getting basic finite state machine behaviour using a Java enum.
package com.gabrielbauman.gist;
import java.util.Arrays;
/**
* A very simple (but useful) method of getting basic finite state machine behaviour using a Java enum.
*
* <code>
* EnumStateMachine state = NEUTRAL;
* state = state.transitionTo(FIRST);
* state.transitionTo(REVERSE); // IllegalStateException
* </code>
*/
public enum EnumStateMachine {
FIRST,
NEUTRAL,
REVERSE;
static {
NEUTRAL.allowTransitionTo(FIRST, REVERSE);
FIRST.allowTransitionTo(NEUTRAL);
REVERSE.allowTransitionTo(NEUTRAL);
}
// Everything below this line is boilerplate. It's too bad we don't have "abstract base enums" in Java.
private EnumStateMachine[] possibleTransitions;
private void allowTransitionTo(EnumStateMachine... allowableStates) {
possibleTransitions = allowableStates;
}
public boolean canTransitionTo(EnumStateMachine anotherState) {
return Arrays.stream(possibleTransitions).anyMatch(anotherState::equals);
}
public EnumStateMachine transitionTo(EnumStateMachine newState) {
if (!canTransitionTo(newState))
throw new IllegalStateException(String.format("Illegal transition: %s -> %s", this, newState));
return newState;
}
}
package com.gabrielbauman.gist;
import static com.gabrielbauman.gist.EnumStateMachine.FIRST;
import static com.gabrielbauman.gist.EnumStateMachine.NEUTRAL;
import static com.gabrielbauman.gist.EnumStateMachine.REVERSE;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
public class EnumStateMachineTest {
@Test
public void testTransitionFromNeutralToFirst() {
assertThat(NEUTRAL.canTransitionTo(FIRST), is(equalTo(true)));
assertThat(NEUTRAL.transitionTo(FIRST), is(equalTo(FIRST)));
}
@Test
public void testTransitionFromNeutralToReverse() {
assertThat(NEUTRAL.canTransitionTo(REVERSE), is(equalTo(true)));
assertThat(NEUTRAL.transitionTo(REVERSE), is(equalTo(REVERSE)));
}
@Test
public void testTransitionFromFirstToNeutral() {
assertThat(FIRST.canTransitionTo(NEUTRAL), is(equalTo(true)));
assertThat(FIRST.transitionTo(NEUTRAL), is(equalTo(NEUTRAL)));
}
@Test
public void testTransitionFromReverseToNeutral() {
assertThat(REVERSE.canTransitionTo(NEUTRAL), is(equalTo(true)));
assertThat(REVERSE.transitionTo(NEUTRAL), is(equalTo(NEUTRAL)));
}
@Test(expected = IllegalStateException.class)
public void testTransitionFromNeutralToNeutral() {
assertThat(NEUTRAL.canTransitionTo(NEUTRAL), is(equalTo(false)));
NEUTRAL.transitionTo(NEUTRAL);
}
@Test(expected = IllegalStateException.class)
public void testTransitionFromFirstToFirst() {
assertThat(FIRST.canTransitionTo(FIRST), is(equalTo(false)));
FIRST.transitionTo(FIRST);
}
@Test(expected = IllegalStateException.class)
public void testTransitionFromFirstToReverse() {
assertThat(FIRST.canTransitionTo(REVERSE), is(equalTo(false)));
FIRST.transitionTo(REVERSE);
}
@Test(expected = IllegalStateException.class)
public void testTransitionFromReverseToReverse() {
assertThat(REVERSE.canTransitionTo(REVERSE), is(equalTo(false)));
REVERSE.transitionTo(REVERSE);
}
@Test(expected = IllegalStateException.class)
public void testTransitionFromReverseToFirst() {
assertThat(REVERSE.canTransitionTo(FIRST), is(equalTo(false)));
REVERSE.transitionTo(FIRST);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment