SpazioCodice

Cet article décrit une approche pour éliminer la logique conditionnelle explicite dans votre code en utilisant les design patterns Strategy et State.

IF-LESS code: (Encore une fois) le Pattern State

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();
  }
}
				
			

Share this post

Laisser un commentaire

En savoir plus sur SpazioCodice

Abonnez-vous pour poursuivre la lecture et avoir accès à l’ensemble des archives.

Poursuivre la lecture