Skip to content

Finite state machine #222

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions finite-state-machine/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.5.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>finite-state-machine</artifactId>


</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package com.iluwatar.fsm;

import java.util.HashMap;
import java.util.Map;

/**
* Represents a simple finite state machine implementation with a custom context and where events and states are defined as enumerations.
*
* Created by Stephen Lazarionok.
*/
public final class FiniteStateMachine<C> {

private Enum<?> state;

private C context;

private Map<TransitionMappingKey, TransitionMappingValue> transitions;

public Enum<?> getState() {
return state;
}

public void raiseEvent(Enum<?> event) {

System.out.println("Event '" + event + "' has been raised...");

// Tries to find the transition mapping by the start state and the event raised.
final TransitionMappingValue transitionMappingValue = transitions.get(new TransitionMappingKey(event, state));
if (transitionMappingValue != null) {

// Gets the target state
final Enum<?> targetState = transitionMappingValue.getState();

// Invokes the transition handler if exists
if (transitionMappingValue.getTransitionHandler() != null) {
transitionMappingValue.getTransitionHandler().handle(state, targetState, context);
}

final Enum<?> oldState = this.state;
this.state = targetState;
System.out.println("Transition from '" + oldState + "' to '" + state + "' triggered by '"+ event + "' event has been completed.");
}
else {
System.out.println("No available transitions found by event '" + event + "' from state '" + state + "'.");
}
}

private static final class TransitionMappingKey {

private Enum<?> event;

private Enum<?> state;

private TransitionMappingKey(Enum<?> event, Enum<?> state) {
this.event = event;
this.state = state;
}

public Enum<?> getEvent() {
return event;
}

public Enum<?> getState() {
return state;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

TransitionMappingKey that = (TransitionMappingKey) o;

if (event != null ? !event.equals(that.event) : that.event != null) return false;
if (state != null ? !state.equals(that.state) : that.state != null) return false;

return true;
}

@Override
public int hashCode() {
int result = event != null ? event.hashCode() : 0;
result = 31 * result + (state != null ? state.hashCode() : 0);
return result;
}
}

private static final class TransitionMappingValue<C> {

private TransitionHandler<C> transitionHandler;

private Enum<?> state;

public TransitionMappingValue(TransitionHandler<C> transitionHandler, Enum<?> state) {
this.transitionHandler = transitionHandler;
this.state = state;
}

public TransitionHandler<C> getTransitionHandler() {
return transitionHandler;
}

public Enum<?> getState() {
return state;
}
}

private FiniteStateMachine() {
}

/**
* A builder for custom finite state machines.
*
*/
public static final class Builder<C> {


private Enum<?> state;

private C context;

private Map<TransitionMappingKey, TransitionMappingValue> transitions = new HashMap<TransitionMappingKey, TransitionMappingValue>();

private Builder() {
}

/**
* Creates a new builder.
* @return a new builder instance.
*/
public static Builder create() {
return new Builder();
}

/**
* Defines an initial state.
* @param state
* @return
*/
public Builder withDefaultState(final Enum<?> state) {
this.state = state;
return this;
}

/**
* Add a transition.
* @param from - 'from' state
* @param event - an event that triggers the transition
* @param to - 'to' state
* @param transitionHandler - a transition handler.
* @return
*/
public Builder addTransition(final Enum<?> from, final Enum<?> event, final Enum<?> to, final TransitionHandler<C> transitionHandler) {
transitions.put(new TransitionMappingKey(event, from), new TransitionMappingValue(transitionHandler, to));
return this;
}

/**
* Defines the context of the FSM. Might be used to pass external services to handle transitions within the FSM.
* @param context
* @return
*/
public Builder withContext(final C context) {
this.context = context;
return this;
}

/**
* Builds a FSM based on the rules defined.
* @return
*/
public FiniteStateMachine<C> build() {
final FiniteStateMachine<C> result = new FiniteStateMachine<C>();
result.context = context;
result.transitions = transitions;
result.state = state;
return result;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.iluwatar.fsm;

/**
* Represents a hook that handles transitions from one to another state.
*
* Created by Stephen Lazarionok.
*/
public interface TransitionHandler<C> {

/**
* Handles a transition from one to another state.
*
* @param from - an initial state
* @param to - a target state
* @param context - a custom context passed.
*/
void handle(Enum<?> from, Enum<?> to, C context);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.iluwatar.fsm.sample;

/**
* Created by Stephen Lazarionok.
*/
public class Car {

public void drive() {
System.out.println("***************************");
System.out.println("***************************");
System.out.println("Driving...");
System.out.println("***************************");
System.out.println("***************************");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.iluwatar.fsm.sample;

import com.iluwatar.fsm.FiniteStateMachine;
import com.iluwatar.fsm.TransitionHandler;

import java.util.HashMap;
import java.util.Map;


/**
* A demo application that shows how to work with traffic lights.
* <p/>
* Created by Stephen Lazarionok.
*/
public class Sample {

public static void main(final String[] args) {

// Build a simple context based on map
final Map<String, Object> context = new HashMap<String, Object>();
context.put("mycar", new Car());

// A handler that just logs a transition
final TransitionHandler<Map<String, Object>> logTransitionHandler = new TransitionHandler<Map<String, Object>>() {

@Override
public void handle(Enum<?> from, Enum<?> to, Map<String, Object> context) {
System.out.println("Performing transition from '" + from + "' to '" + to + "'...");
}
};

// A handler that starts driving my car
final TransitionHandler<Map<String, Object>> startDrivingHandler = new TransitionHandler<Map<String, Object>>() {

@Override
public void handle(Enum<?> from, Enum<?> to, Map<String, Object> context) {

Car.class.cast(context.get("mycar")).drive();
}
};

final FiniteStateMachine<Map<String, ?>> trafficLightFsm = FiniteStateMachine.Builder.create()
.withDefaultState(TrafficLightState.RED)
.addTransition(TrafficLightState.GREEN, TrafficLightEvent.SWITCH, TrafficLightState.YELLOW, logTransitionHandler)
.addTransition(TrafficLightState.YELLOW, TrafficLightEvent.SWITCH, TrafficLightState.RED, logTransitionHandler)
.addTransition(TrafficLightState.RED, TrafficLightEvent.SWITCH, TrafficLightState.YELLOW, logTransitionHandler)
.addTransition(TrafficLightState.YELLOW, TrafficLightEvent.SWITCH, TrafficLightState.GREEN, startDrivingHandler)
.withContext(context)
.build();

trafficLightFsm.raiseEvent(TrafficLightEvent.SWITCH);
trafficLightFsm.raiseEvent(TrafficLightEvent.SWITCH);

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.iluwatar.fsm.sample;

/**
* Contains all the events that may happen with traffic light.
*
* Created by Stephen Lazarionok.
*/
public enum TrafficLightEvent {
SWITCH
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.iluwatar.fsm.sample;

/**
* Contains all the states of traffic light.
*
* Created by Stephen Lazarionok.
*/
public enum TrafficLightState {
RED, YELLOW, GREEN
}