We have an interface named IService that represents a generic service. The interface offers three methods : start(), stop() and isRunning().
public interface IService {
void start(); // Starts the service.
void stop(); // Stops the service.
boolean isRunning(); // Returns true if the service is running.
}
While looking at the following implementation, I wasn't satisfied about the conditional statements on start() and stop() method :
public class ServiceImpl implements IService {
private volatile boolean started;
private Thread thread;
public synchronized void start() {
if (started) {
return;
}
started = true;
thread = ... // Create your daemon implementation.
thread.setDaemon(true);
thread.start();
}
public synchronized void stop()
{
if (!started) {
return;
}
started = false;
thread.interrupt();
try {
thread.join();
}
catch (InterruptedException ignore) {
}
}
public synchronized boolean isRunning() {
return started;
}
}
To be honest, what I don't like is :
- Maintaining the state of the service in a boolean member and therefore relying on that in order to know if the service is alive or not (the isRunning method)
- Conditional logic used for checking everytime the (start & stop) methods are called if the service is alive or not. I think the service should know that without using a boolean member to remember! An example : if you are driving a car, how do you know that it's running? You know that because it's running and you're driving! ;-) and not because after turned it on you put a post-it on the dashboard with "RUNNING" written over!
I mean : it's the status itself that suggests you what's going on!
As result of that, I prefer this version of the service implementation:
public class IfLessService implements IService {
private Thread _watcher;
/**
* RUNNING STATE.
*/
private final IService running = new IService() {
public boolean isRunning()
{
return true; // We are inside the running state and so...
}
public void start() {
// Nothing to do here...it is already started.
}
public void stop() {
_watcher.interrupt();
try {
_watcher.join();
}
catch (InterruptedException ignore)
{
}
state = notRunning;
}
};
/**
* NOT RUNNING STATE.
*/
private final IService notRunning = new IService() {
public boolean isRunning() {
return false; // We are inside the not running state and so...
}
public synchronized void start() {
_watcher = ...// Create your daemon implementation...
_watcher.setDaemon(true);
_watcher.start();
// ...and make a state change too...from NOT-RUNNING to RUNNING.
state = running;
}
public void stop() {
// Nothing to do here...it is already stopped.
}
};
// Default initial state is stopped (not running).
private IService state = notRunning;
public synchronized void start() {
state.start(); // Current state delegation
}
public synchronized void stop() {
state.stop(); // Current state delegation
}
public boolean isRunning() {
return state.isRunning(); // Current state delegation
}
}
Now the service implementation is delegating the execution of the IService methods to the current internal state. Those states are themselves implementors of IService interface. We have two states : running and notRunning.
Note that the responsibility of each state is not only to manage the "state" of the object at a specific moment but also to provide an eventual state transition: for example when the service is not running (state = notRunning), if you call the start() method there will be a state transition (state = running).
Any comment would be very very appreciated...