Assignment : Itérer sur une Liste de chaînes et afficher chaque élément avec les règles suivantes :
- La première valeur doit être affichée telle quelle
- Les autres éléments doivent être affichés en majuscules
- À la fin de la boucle, affichez “Exit”
Une première version
public class Service {
public void loop(List strings) {
int index = 0;
for (String string : strings) {
if (index == 0) {
System.out.println(string);
} else {
System.out.println(string.toUpperCase());
}
index++;
}
System.out.println("Exit");
}
}
Ok, c’est simple, clair et concis; essayons de “compliquer” un peu les choses.
State Pattern
Les tâches exécutées à chaque itération dans l’exemple sont triviales, mais dans un scénario réel, cela pourrait être compliqué : pourquoi ne pas isoler ces tâches dans une méthode séparée ou même dans une classe séparée ?
Introduisons une interface Iteration qui représente la tâche exécutée à chaque itération donnée
.
interface Iteration {
void execute(String value);
}
Trois tâches, trois implémentations initiales :
Iteration firstIteration = new Iteration() {
public void execute(String value) {
System.out.println(value);
}
}
Iteration middleIteration = new Iteration() {
public void execute(String value) {
System.out.println(value.toUppercase());
}
}
private final Iteration noIteration = new Iteration() {
public void execute() {
System.out.println("Exit");
}
}
Maintenant, chaque tâche doit être responsable de l’exécution de sa logique et, de plus, elle doit fournir un mécanisme pour passer à l’itération suivante.
Une manière de faire cela est d’introduire un petit changement dans l’interface Iteration et dans la classe extérieure qui gère les tâches :
interface Iteration {
void execute();
}
public class Loop {
private final Iterator iterator;
...
public Loop(final List strings) {
iterator = strings.iterator();
}
Iteration firstIteration = new Iteration() {
public void execute() {
System.out.println(iterator.next());
}
}
Iteration middleIteration = new Iteration() {
public void execute() {
System.out.println(iterator.next().toUppercase());
}
}
Iteration lastIteration = new Iteration() {
public void execute() {
System.out.println("Exit");
}
}
}
À ce stade, nous devons maintenir une référence à l’itération actuelle. Introduisons un autre membre d’instance : currentIteration, qui est initialement défini sur firstIteration.
private Iteration currentIteration = firstIteration;
Il manque encore deux choses. Premièrement, la classe Loop doit disposer d’une méthode pour démarrer l’exécution de la boucle :
public void start() {
currentIteration.execute();
}
Ensuite, chaque tâche doit décider comment avancer à l’itération suivante. Cette logique peut être isolée dans une méthode composée :
private void nextIteration() {
currentIteration = iterator.hasNext()
? middleIteration
: noIteration;
currentIteration.execute();
}
Je sais : il y a encore une logique conditionnelle ; cependant, ce n’est pas exactement la même que dans la première version de Loop : il n’y a pas d’index ni de variables temporaires, et chaque classe Iteration contient exactement ce qu’il faut faire à chaque cycle d’itération.
Le code dans cet exemple est juste un exercice, et je suis d’accord avec vous que les bénéfices ne sont pas évidents ; cela parce que le problème sous-jacent est très simple.
Voici la version finale de la classe Loop :
interface Iteration {
void execute();
}
public class Loop {
Iteration firstIteration = new Iteration() {
public void execute() {
System.out.println(iterator.next());
nextIteration();
}
}
Iteration middleIteration = new Iteration() {
public void execute() {
System.out.println(iterator.next().toUppercase());
nextIteration();
}
}
Iteration lastIteration = new Iteration() {
public void execute() {
System.out.println("Exit");
}
}
private final Iterator iterator;
private Iteration currentIteration = firstIteration;
public Loop(final List strings) {
iterator = strings.iterator();
}
public void start() {
currentIteration.execute();
}
private void nextIteration() {
currentIteration = iterator.hasNext()
? middleIteration
: noIteration;
currentIteration.execute();
}
}