Taming the processing loop
In Mikamai we do a lot of reasearch on non conventional hardware, we make prototypes or create unsual interfaces that are very domain specific.
To quickly sketch ideas, we often rely on Processing, it’s super easy and its loop based execution model gives the feeling of programming a video game.
The drawback is that it is so fast to get something working, that you will be tempted to make the mistake of creating a polished prototpe.
Your prototype code ends up in production and there’s no way back from there.
To resist the temptation of releasing a blob of code, I borrowed a technique from one of the Rob Pike’s talks to keep things easy, while keeping them clean at the same time.
It is basically an implementation of a state machime.
We’re gonna have a StateMachine
class that handles the inputs and the state changes, and several state classes that implement the State
interface.
The interface is very simple and contains only one method
interface State {
public State nextState();
}
The loop of our Processing application is really simple too
StateMachine sm = new StateMachine(initialstate);
void draw() {
sm = sm.nextState();
}
and this is the most basic implementation possible of the StateMachine
class
class StateMachine(State initialstate) {
private State currentstate;
StateMachine(State initialstate) {
this.currentstate = initialstate;
}
public StateMachine nextState() {
this.currentstate = this.currentstate.nextState();
return this;
}
}
Each class must implement the nextState
method and return an istance of the next state that will be executed.
With this knowledge in mind, this is how you build an infinite loop inside an inifinite loop
class InfiniteLoopState implements State {
public State nextState() {
return this;
}
}
But we can do better!
How about a ping pong?
class Ping implements State {
public State nextState() {
println("ping?");
return new PongState();
}
}
class Pong implements State {
public State nextState() {
println("pong!");
return new PingState();
}
}
We moved the logic of the application out of the central switch/case
statement in the draw
function and deconstruted it into smaller pieces, that only know about themselves and the next state they are going to emit.
As long as your state classes implement the State
interface you can exapnd the concept to fit your needs.
For example, if you need to keep track of the environment and/or the previous state, you can adjust the State
interface to support it.
interface State {
public State nextState(State previousstate, StateMachine sm);
// StateMachine holds the environment for us
}
and modify StateMachine
accordingly
public StateMachine nextState() {
this.currentstate = this.currentstate.nextState(this.currentstate, this);
return this;
}
TL;DR: state machines are easy, use them!
You can find examples on how to use this pattern and how to add more features in the github repository.