Dans cet article de blog, nous décrivons une implémentation exemple du State Design Pattern.
Contexte
Comme point de départ, il y a une interface nommée IService qui représente un service générique. L’interface offre trois méthodes : start(), stop() et isRunning().
public interface IService {
void start();
void stop();
boolean isRunning();
}
Voici une implémentation :
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;
}
}
Le code ci-dessus fonctionne… mais qu’est-ce qui n’est pas si bien ?
Maintenir l’état du service dans une variable booléenne et s’appuyer sur cela pour savoir si le service est en cours d’exécution ou non.
La logique conditionnelle au début des méthodes start() et stop() . Un objet doit être conscient de son état actuel : si vous conduisez une voiture, comment savez-vous qu’elle est en marche ? Parce qu’elle bouge, parce que vous conduisez, pas parce que vous avez mis un post-it sur le siège (“Attention ! Elle est en marche !“).
State Pattern
Le State pattern est un modèle de conception comportemental de modèle de conception logicielle. Il encapsule l’état interne d’un objet qui peut modifier son comportement lorsqu’une transition se produit.
Outre la représentation des états possibles d’un objet donné, chaque implémentation concrète de l’état définit également les règles de transition d’un état à un autre. Pour cette raison, le State pattern est très proche du concept des machines à états finis.
Le code suivant de l’interface IService est une implémentation du 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();
}
}
L’implémentation du service délègue l’exécution des méthodes IService à l’état interne (actuel).
Chaque état possible est lui-même une implémentation de l’interface IService interface. Il s’agit d’un scénario “binaire”, donc il n’y a que deux classes d’état : running et notRunning.
Notez que la responsabilité de chaque State est de gérer le comportement de l’objet possesseur à un moment donné, mais aussi de gérer, lorsque nécessaire, une transition d’état vers un autre State : par exemple, lorsque le service n’est pas en cours d’exécution (c’est-à-dire currentState = notRunning), si vous appelez la méthode start() il y aura une transition d’état (de notRunning à running). ). Une fois que le service est démarré, appeler à nouveau la méthode start() n’aura aucun effet.