• 15 heures
  • Facile

Ce cours est visible gratuitement en ligne.

Ce cours est en vidéo.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

Mis à jour le 28/01/2019

Répétez une action avec des boucles

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Dans ce chapitre nous allons proposer à nos utilisateurs de commander plusieurs menus.

Nous pourrions le faire avec des conditions et des méthodes sous la forme :

if (menuQuantity == 1) {
	runMenu();
} else if (menuQuantity == 2) {
	runMenu();
    runMenu();
} else if ...

Cela deviendrait vite fastidieux, et si nous devons gérer jusqu'à 100 menus, le code risque d'être énorme.
Ce type de problématique revient souvent dans le développement. Pour y répondre un outil existe : les boucles.

Une partie du cours d'algorithmique traite des boucles. Prenez le temps de regarder ce cours, cela vous aidera à mieux comprendre la suite.

Pour récupérer une version propre du projet prêt pour ce chapitre, vous pouvez importer le projet git suivant : MyMenu - Step 5

while

Le premier format de boucle que nous allons voir est le  while . Il se construit de la manière suivante :

while (condition à respecter pour la boucle continue) {
	code à exécuter
}

On a :

  • le mot-clé  while,

  • entre parenthèses () une condition qui va dire si la boucle continue à être exécutée, () une condition qui va dire si la boucle continue à être exécutée,

  • entre accolades {}, le code à exécuter.

Nous allons coder un exemple dans JShell pour que vous compreniez mieux.

Commençons par créer une variable nommée  counter . Elle servira à compter le nombre de passages dans la boucle.

jshell> int counter = 0;
counter ==> 0

On va ensuite créer une boucle dans laquelle on va passer 5 fois

jshell> while (counter < 5) {
   ...>

On va, à chaque tour de boucle, augmenter la variable counter de 1 pour que celle-ci, au 5ème tour, dépasse la valeur 5 et que notre condition ne soit donc plus juste.

while (counter < 5) {
   ...> counter = counter + 1;
   ...>

Enfin on va écrire du texte pour être certain que notre boucle fonctionne bien. Puis fermer l'accolade.

jshell> while (counter < 5) {
   ...> counter = counter + 1;
   ...> System.out.println("Je suis dans le tour " + counter);
   ...> }
Je suis dans le tour 1
Je suis dans le tour 2
Je suis dans le tour 3
Je suis dans le tour 4
Je suis dans le tour 5

Et voilà notre code a bien été exécuté 5 fois !

Nous allons ajouter à notre code une méthode nommée  runMenus  servant à demander plusieurs menus. Elle se chargera de :

  • demander à l'utilisateur combien de menu il souhaite ;

  •  faire une boucle et appeler  runMenu  autant de fois que nécessaire.

La première chose à faire est de mutualiser certaines ressources. runMenu  et  runMenus  vont tous deux avoir besoin d'un Scanner lisant la console. 

On va donc créer un attribut dans la classe  Order . C'est-à-dire une variable accessible de partout dans la classe  Order . Pour faire cela, il faut juste déclarer notre  Scanner  en dehors de toute méthode :

public class Order {
    Scanner sc = new Scanner(System.in);
    
    ...
    
    /**
     * Run asking process for a menu.
     */
    public void runMenu() {
        this.displayAvailableMenu();
        int nbMenu = sc.nextInt();
        this.displaySelectedMenu(nbMenu);
        switch (nbMenu) {
            ...
        }
    }
}

Vous pouvez maintenant utiliser  sc  dans  runMenu  et  runMenus .

C'est parfois compliqué de savoir si une variable doit être attribut ou non. Pour créer un attribut, il faut toujours se poser plusieurs questions :

  • Est-ce que ma variable représente un composant de mon objet ?

  • Est-ce que j'ai besoin de cette variable uniquement dans une méthode ou dans plusieurs ?

Les attributs sont aussi représentés dans les diagrammes de classes UML

Diagramme de classes UML avec le Scanner sc via IntelliJ Ultimate
UML avec le Scanner sc

Voici 2 tests pour  runMenus  :

@Test
public void Given_OneMenuChikenWithFriesAndWaterInStandardInput_When_MenusIsRun_Then_DisplayCorrectProcess() {
    System.setIn(new ByteArrayInputStream("1\n1\n2\n3\n".getBytes()));
    order = new Order();
    order.runMenus();
    String[] output = outContent.toString().replace("\r\n", "\n").split("\n");
    assertEquals("Vous avez choisi comme menu : poulet", output[6]);
    assertEquals("Vous avez choisi comme accompagnement : frites", output[12]);
    assertEquals("Vous avez choisi comme boisson : soda", output[18]);
}
@Test
public void Given_TwoMenu_BeefWithVegetable_VegetarianWithNoRiceAndSparklingWaterInStandardInput_When_MenusIsRun_Then_DisplayCorrectProcess() {
    System.setIn(new ByteArrayInputStream("2\n2\n1\n3\n2\n2\n".getBytes()));
    order = new Order();
    order.runMenus();
    String[] output = outContent.toString().replace("\r\n", "\n").split("\n");
    assertEquals("Vous avez choisi comme menu : boeuf", output[6]);
    assertEquals("Vous avez choisi comme accompagnement : légumes frais", output[12]);
    assertEquals("Vous avez choisi comme menu : végétarien", output[18]);
    assertEquals("Vous avez choisi comme accompagnement : pas de riz", output[23]);
    assertEquals("Vous avez choisi comme boisson : eau gazeuse", output[29]);
}

 Générez automatiquement  runMenus  via IntelliJ (alt +Entrée en positionnant le curseur sur  runMenus ) :

/**
 * Run asking process for several menus.
 */
public void runMenus() {
}

Voici la solution avec une boucle  while  :

/**
 * Run asking process for several menus.
 */
public void runMenus() {
    System.out.println("Combien souhaitez vous commander de menu ?");
    int menuQuantity = sc.nextInt();
    int counter = 0;
    while (counter < menuQuantity) {
        runMenu();
        counter = counter + 1;
    }
}

Les tests fonctionnent mais votre programme toujours pas. Il faut changer la méthode main :

public static void main(String[] args) {
    Order order = new Order();
    order.runMenus();
}

Juste un petit  s  à ajouter ;). 

Et bien sûr, voici notre diagramme actualisé :

Diagramme de classes UML avec runMenus via IntelliJ Ultimate
UML avec runMenus

for

Il existe d'autres types de boucle. Fondamentalement, le  for  a le même rôle que le  while  : effectuer une boucle

Comment choisir entre  for  et  while  ? On utilisera un  for  si :

  1. Le nombre d'itérations est connu à l'avance

  2. Il n'y a pas d'autres conditions qui peuvent justifier d'interrompre le traitement

S'il est prévu d'interrompre la boucle en plein traitement,  une fois une certaine valeur trouvée, par exemple, on utilisera un  while  .

Et bien oui, une fois que l'utilisateur a saisi le nombre de menus qu'il souhaite, nous savons déjà combien de tours de boucle il faut faire.

Le  for  est construit comme ceci :

for (int counter = 0; counter < 5; counter = counter + 1) {
   code à exécuter
}

 C'est un peu plus compliqué à comprendre, on va décomposer :

  • le mot-clé for , puis on ouvre la parenthèse( ;

  • la création de la variable qui va servir de compteur. Ici c'est une variable de type  int  qui se nomme  counter  et qui a pour valeur  0 ;

  • un point virgule; ;

  • la condition à respecter pour rester dans la boucle. Ici on regarde que le compteur soit inférieur à 5 ;

  • un point virgule ; ;

  • la façon dont notre compteur est modifié à chaque tour. Ici, on ajoute 1 à notre compteur à chaque tour ;

  • fermeture de la parenthèse ) ;

  • entre accolades {} le code à exécuter.

Dans JShell cela donne :

jshell> for (int counter = 0; counter < 5; counter++) {
   ...> System.out.println("Je suis dans le tour " + counter);
   ...> }
Je suis dans le tour 0
Je suis dans le tour 1
Je suis dans le tour 2
Je suis dans le tour 3
Je suis dans le tour 4

Modifiez  runMenus  pour utiliser  for . Pas besoin de modifier ou d'ajouter de test, le comportement de  runMenus  ne change pas

public void runMenus() {
    System.out.println("Combien souhaitez vous commander de menu ?");
    int menuQuantity = sc.nextInt();
    for (int i = 0; i < menuQuantity; i++) {
        runMenu();
    }
}

Le gros intérêt d'utiliser un  for  est de voir directement qu'on a une boucle qui dure un nombre de tours connu à l'avance.

do while

Il existe un dernier type de boucle : le  do   while . C'est une boucle qui ressemble beaucoup à while à la différence que la condition de boucle est effectuée à la fin du code à exécuter et pas au début. 

Cela donne :

do {
    code à exécuter
} while (condition de sortie);

Je pense que normalement vous comprenez chaque élément. Faisons un test dans JShell :

jshell> do {
   ...> System.out.println("tour : " + i);
   ...> i++;
   ...> } while(i < 5)
tour : 0
tour : 1
tour : 2
tour : 3
tour : 4

On va se servir de cet outil pour être certain que notre utilisateur a bien saisi l'un des choix qui lui est proposé. On va lui afficher l'ensemble des menus et tant qu'il n'a pas saisi une valeur comprise entre 1 et 3, on recommence.

On ajoute un test sur  runMenu  pour être sûr que cela fonctionne bien :

@Test
public void Given_BadMenu_When_MenuIsRun_Then_ReAskMenu() {
    System.setIn(new ByteArrayInputStream("4\n1\n2\n3\n".getBytes()));
    order = new Order();
    order.runMenu();
    String[] output = outContent.toString().replace("\r\n", "\n").split("\n");
    assertEquals("Vous n'avez pas choisi de menu parmi les choix proposés", output[5]);
    assertEquals("Vous avez choisi comme menu : poulet", output[6]);
}

Lancez le test, il ne fonctionne pas pour l'instant.

Il faut maintenant modifier runMenu .

public void runMenu() {
    this.displayAvailableMenu();
    int nbMenu;
    do {
        nbMenu = sc.nextInt();
        this.displaySelectedMenu(nbMenu);
        switch (nbMenu) {
            case 1:
                displayAvailableSide(true);
                int nbSide = sc.nextInt();
                displaySelectedSide(nbSide, true);
                displayAvailableDrink();
                int nbDrink = sc.nextInt();
                displaySelectedDrink(nbDrink);
                break;
            case 2:
                displayAvailableSide(true);
                nbSide = sc.nextInt();
                displaySelectedSide(nbSide, true);
                break;
            case 3:
                displayAvailableSide(false);
                nbSide = sc.nextInt();
                displaySelectedSide(nbSide, false);
                displayAvailableDrink();
                nbDrink = sc.nextInt();
                displaySelectedDrink(nbDrink);
                break;
        }
    } while (nbMenu < 1 || nbMenu > 3);
}

On fait maintenant ça pour les accompagnements et les boissons.

Voici 3 tests pour chaque type de menu :

@Test
public void Given_ChikenWithBadSideAndBadDrink_When_MenuIsRun_Then_ReAskSideAndDrink() {
    System.setIn(new ByteArrayInputStream("1\n4\n2\n-1\n3\n".getBytes()));
    order = new Order();
    order.runMenu();
    String[] output = outContent.toString().replace("\r\n", "\n").split("\n");
    assertEquals("Vous avez choisi comme menu : poulet", output[5]);
    assertEquals("Vous n'avez pas choisi d'accompagnement parmi les choix proposés", output[11]);
    assertEquals("Vous avez choisi comme accompagnement : frites", output[12]);
    assertEquals("Vous n'avez pas choisi de boisson parmi les choix proposés", output[18]);
    assertEquals("Vous avez choisi comme boisson : soda", output[19]);
}
@Test
public void Given_BeefWithBadSide_When_MenuIsRun_Then_ReAskSideAndDrink() {
    System.setIn(new ByteArrayInputStream("2\n4\n2\n".getBytes()));
    order = new Order();
    order.runMenu();
    String[] output = outContent.toString().replace("\r\n", "\n").split("\n");
    assertEquals("Vous avez choisi comme menu : boeuf", output[5]);
    assertEquals("Vous n'avez pas choisi d'accompagnement parmi les choix proposés", output[11]);
    assertEquals("Vous avez choisi comme accompagnement : frites", output[12]);
}
@Test
public void Given_VegetarianWithBadSideAndBadDrink_When_MenuIsRun_Then_ReAskSideAndDrink() {
    System.setIn(new ByteArrayInputStream("3\n3\n2\n-1\n3\n".getBytes()));
    order = new Order();
    order.runMenu();
    String[] output = outContent.toString().replace("\r\n", "\n").split("\n");
    assertEquals("Vous avez choisi comme menu : végétarien", output[5]);
    assertEquals("Vous n'avez pas choisi d'accompagnement parmi les choix proposés", output[10]);
    assertEquals("Vous avez choisi comme accompagnement : pas de riz", output[11]);
    assertEquals("Vous n'avez pas choisi de boisson parmi les choix proposés", output[17]);
    assertEquals("Vous avez choisi comme boisson : soda", output[18]);
}

 Et voici le nouveau code de  runMenu  :

public void runMenu() {
    this.displayAvailableMenu();
    int nbMenu;
    do {
        nbMenu = sc.nextInt();
        this.displaySelectedMenu(nbMenu);
        switch (nbMenu) {
            case 1:
                displayAvailableSide(true);
                int nbSide;
                do {
                    nbSide = sc.nextInt();
                    displaySelectedSide(nbSide, true);
                } while (nbSide < 1 || nbSide > 3);
                displayAvailableDrink();
                int nbDrink;
                do {
                    nbDrink = sc.nextInt();
                    displaySelectedDrink(nbDrink);
                } while (nbDrink < 1 || nbDrink > 3);
                break;
            case 2:
                displayAvailableSide(true);
                do {
                    nbSide = sc.nextInt();
                    displaySelectedSide(nbSide, true);
                } while (nbSide < 1 || nbSide > 3);
                break;
            case 3:
                displayAvailableSide(false);
                do {
                    nbSide = sc.nextInt();
                    displaySelectedSide(nbSide, false);
                } while (nbSide < 1 || nbSide > 2);
                displayAvailableDrink();
                do {
                    nbDrink = sc.nextInt();
                    displaySelectedDrink(nbDrink);
                } while (nbDrink < 1 || nbDrink > 3);
                break;
        }
    } while (nbMenu < 1 || nbMenu > 3);
}

Beeerk >_< le code devient incompréhensible ! On arrange ça dans le prochain chapitre !

Exemple de certificat de réussite
Exemple de certificat de réussite