In this blog post we describe an example implementation of the State Design Pattern.
Context
As a starting point there’s an interface named IService which represents a generic service. The interface offers three methods : start(), stop() and isRunning().
public interface IService {
void start();
void stop();
boolean isRunning();
}
This is an implementation:
public class ServiceImpl implements IService {
private volatile boolean started;
private Thread thread;
@Override
public synchronized void start() {
if (started) {
return;
}
started = true;
thread = ... // Create a daemon thread instance
thread.setDaemon(true);
thread.start();
}
@Override
public synchronized void stop() {
if (!started) {
return;
}
started = false;
thread.interrupt();
try {
thread.join();
}
catch (InterruptedException ignore) {
...
}
}
@Override
public synchronized boolean isRunning() {
return started;
}
}
The code above works…however what is not so good?
Maintaining the state of the service in a boolean variable and relying on that in order to know if the service is running or not.
The conditional logic at the beginning of start() and stop() methods. An object should be aware about its current state: if you are driving a car, how do you know that it’s running? Because it’s moving, because you’re driving, not because you put a post-it on the seat (“Caution! It’s running!“).
State Pattern
The State pattern is a behavioral Software Design Pattern. It encapsulates the internal state of the object that can alter its behaviour when a transition happens.
Other than representing the possible states of a given object, each concrete State implementor defines also the rules for transitioning from one state to another. For that reason the State pattern is very close to the concept of finite-state machines.
The following code of the IService interface is an implementation of the State Pattern:
public class IfLessService implements IService {
private Thread _watcher;
private final IService running = new IService() {
public boolean isRunning()
{
return true;
}
public void start() {
// Nothing to do here...it is already started.
}
public void stop() {
_watcher.interrupt();
try {
_watcher.join();
}
catch (InterruptedException ignore)
{
...
}
state = notRunning;
}
};
private final IService notRunning = new IService() {
public boolean isRunning() {
return false;
}
public synchronized void start() {
_watcher = ...
_watcher.setDaemon(true);
_watcher.start();
// ...make a state change
// from NOT-RUNNING to RUNNING.
state = running;
}
public void stop() {
// Nothing to do here...it is already stopped!
}
};
// Default initial state is stopped.
private IService state = notRunning;
public synchronized void start() {
state.start();
}
public synchronized void stop() {
state.stop();
}
public boolean isRunning() {
return state.isRunning();
}
}
The service implementation delegates the execution of the IService methods to the (current) internal state.
Each possible State is itself an implementation of the IService interface. This is a “binary” scenario so there are only two State classes: running and notRunning.
Note the responsibility of each State is to manage the behaviour of the owning object at a specific moment, but also to manage, when needed, a state transition towards another State: for example, when the service is not running (i.e. currentState = notRunning), if you call the start() method there will be a state transition (from notRunning to running). Once the service is started, calling the start() method again won’t have any effect.