Keyboard shortcuts

Touchez ← ou → pour naviguer les chapitres

Touchez S ou / pour chercher dans le livre

Touchez ? pour afficher ce message

Touchez Esc pour masquer ce message

Accueil > Programmer avec Java > Décomposition et modularité >

📚 PortĂ©e des variables

Survol et attentes

Définitions
PortĂ©e d’une variable
l’étendue du code source oĂč le nom de variable est reconnue, se limitant aux accolades {} du bloc qui le contiennent. Intentionnellement, la portĂ©e des variables est limitĂ©e pour mieux contrĂŽler l’accĂšs aux informations et pour permettre la rĂ©utilisation des mĂȘmes noms de variables dans diffĂ©rentes parties du code source sans crĂ©er des conflits de noms.
Variable globale
une variable déclarée en dehors de toutes les méthodes. Toutes les méthodes dans le fichier peuvent accéder directement à ces variables.
Variable locale
une variable dĂ©clarĂ©e dans une mĂ©thode ou, Ă  l’intĂ©rieur des mĂ©thodes, dans une structure de contrĂŽle (incluant leurs dĂ©clarations). C’est pourquoi les mĂ©thodes doivent recevoir des arguments et retourner des valeurs pour passer de l’information aux autres mĂ©thodes.
Constante globale
une variable globale qui est dĂ©clarĂ©e final pour indiquer qu’elle ne peut pas ĂȘtre modifiĂ©e. Les constantes globales sont utilisĂ©es pour des valeurs qui ne changent pas dans le programme et qui sont utilisĂ©es dans plusieurs mĂ©thodes. Par exemples : une rĂ©fĂ©rence au Scanner de la console, des valeurs de rĂ©fĂ©rence, des codes de couleur ANSI, des constantes mathĂ©matiques. Nommant ce type de valeur dans une constante globale rend le code plus lisible que d’inclure la valeur littĂ©rale partout oĂč elle est utilisĂ©e.

Objectifs d’apprentissage

À la fin de cette leçon vous devrez ĂȘtre en mesure de :

  • DĂ©crire la diffĂ©rence entre une portĂ©e globale et une portĂ©e locale.
  • Savoir quelle variable, entre deux variables de mĂȘme nom, sera prĂ©fĂ©rĂ©e en considĂ©rant sa portĂ©e.
  • Identifier si une variable spĂ©cifique est accessible ou non dans un endroit donnĂ© du code source.
  • DĂ©crire des raisons de prĂ©fĂ©rer l’utilisation de variables locales Ă  l’utilisation de variables globales.

CritĂšres de succĂšs

  • Je minimise la visibilitĂ© des variables en leur donnant la plus petite portĂ©e nĂ©cessaire, ce qui veut dire utiliser une majoritĂ© de variables locales.
  • J’utilise les variables globales dans mon code lorsque cela rend le code plus lisible et plus facile Ă  maintenir.
  • J’utilise des constantes globales pour les valeurs qui ne changent pas dans mon programme et qui sont utilisĂ©es dans plusieurs mĂ©thodes.

Portée des variables

La portĂ©e d’une variable est synonyme de sa visibilitĂ© : jusqu’à quels endroits dans le code est-ce qu’on peut “voir” cette variable (la nommer sans erreur de syntaxe)?

Les variables globales sont visibles partout dans un programme1. Elles sont dĂ©clarĂ©es Ă  l’extĂ©rieur des mĂ©thodes et on peut les lire et les modifier dans toutes les mĂ©thodes du programme.

C’est un avantage parce qu’on a pas besoin d’échanger cette information entre les mĂ©thodes via des paramĂštres ou des valeurs de retour.

C’est un dĂ©savantage parce n’importe quelle partie de notre code peut modifier cette information, ce qui peut rendre le code difficile Ă  corriger, briser la modularitĂ© du code (les sous-problĂšmes sont maintenant fusionnĂ©s autour de l’information partagĂ©e) ou introduire des problĂšmes de sĂ©curitĂ© (modifications possibles au-delĂ  des intentions originales).

Les variables locales sont seulement visibles dans la mĂ©thode oĂč elles sont dĂ©finies. Les paramĂštres et les variables dĂ©finies dans le corps d’une mĂ©thode sont des variables locales. Jusqu’à prĂ©sent, nous avons seulement utilisĂ© des variables locales dans nos programmes.

Les avantages des variables locales sont que : l’information existe seulement Ă  l’endroit oĂč elle sera directement utile et plusieurs variables dans un programme peuvent avoir le mĂȘme nom sans interfĂ©rence parce que leurs portĂ©es ne se croisent pas.

Le dĂ©savantage est que pour partager l’information locale Ă  une autre mĂ©thode, il faut “passer des messages” avec des arguments/paramĂštres et des valeurs de retour. Cela peut parfois rendre la liste des paramĂštres assez longue que c’est difficile Ă  se rappeler quel paramĂštre va Ă  quelle position.

---------------fichier------------------

déclaration de variable globale


méthode() {

    déclaration de variable locale

}


méthode( les paramÚtres sont des variable locales ) {

}

----------------------------------------

Variables globales

C’est rarement une bonne idĂ©e d’utiliser des variables globales parce que le code devient beaucoup plus dur Ă  dĂ©boguer. Mais il y a quelques cas oĂč c’est une solution bien adaptĂ©e :

La valeur est constante et requise par plusieurs méthodes

On peut alors déclarer une constante globale en le préfixant avec le mot-clé final et en appliquant la convention du nom en majuscules. Les Scanner pour la console, les codes de couleur ANSI et les structures de données (à venir dans une prochaine section du cours) sont des exemples de bons candidats de constantes globales.

Dans ce cas, le fait que la valeur soit constante élimine la possibilité de modification inappropriée et la portée globale peut réduire la complexité et la répétitivité des déclarations et appels de méthodes.

La variable représente un état du programme que plusieurs méthodes doivent consulter ou peuvent modifier

Plusieurs informations dans un jeu tombent dans cette catĂ©gorie. Si ces variables n’étaient pas globales, la liste des paramĂštres pour la plupart des mĂ©thodes deviendrait ingĂ©rable. De plus, la restriction avec Java d’une seule valeur de retour rend l’exportation de plus qu’une modification Ă  ces valeurs, si pas impossible, du moins trĂšs complexe. Cette complexitĂ© additionnelle rendrait le code plus dur Ă  lire, Ă  dĂ©boguer et Ă  modifier. Les risques liĂ©s aux variables globales sont alors moindre que les risques avec l’autre approche.

Exemples de constantes globales

Fichier GlobalScanner.java

import java.util.*;

final Scanner CONSOLE = new Scanner(System.in); // visible globalement

void main() {
    // final Scanner CONSOLE = new Scanner(System.in); // ne serait pas visible dans getName ni getInteger
    String name = getName();
    int age = getInteger("age");

    System.out.printf("%s a %d ans.\n", name, age);
}

String getName() {
    System.out.print("Quel est votre nom? > ");
    return CONSOLE.nextLine();
}

int getInteger(String what) {
    System.out.printf("Entrez un nombre entier pour votre %s > ", what);
    int result = CONSOLE.nextInt(); // pour consommer la valeur
    CONSOLE.nextLine(); // pour consommer le retour de ligne aussi
    return result;
}

Ici le Scanner pour la console est utilisĂ©e dans plusieurs mĂ©thodes et ne change pas de valeur (rĂ©fĂšre toujours Ă  System.in), alors c’est un bon candidat de constant globale.

Notez que la dĂ©claration commence avec final disant Ă  Java que cette valeur ne peut jamais changer durant l’exĂ©cution. Notez aussi qu’on a Ă©crit le nom en majuscules afin de mieux le repĂ©rer comme constante.

Fichier GlobalColorCodes.java

import java.util.*;

final String RED = "\033[0;31m";
final String GREEN = "\033[0;32m";
final String YELLOW = "\033[0;33m";
final String RESET = "\033[0m";

void printRed(String text) {
   System.out.println(RED + text + RESET);
}

void printGreen(String text) {
   System.out.println(GREEN + text + RESET);
}

void printYellow(String text) {
   System.out.println(YELLOW + text + RESET);
}

void main() {
    printRed("Ceci est un message d'erreur!");
    printGreen("Ceci est un message de succĂšs!");
    printYellow("Ceci est un avertissement");

    System.out.printf("%sCe texte%s est en %ssurbrillance%s%s!!!%s", 
            GREEN, RESET, YELLOW, RESET, RED, RESET);
}

Ici on a plusieurs constantes globales pour les différents codes de couleur pour le texte à la console. Ces constantes sont utilisées dans les différentes méthodes, incluant main qui fait un usage sur mesure pour la derniÚre phrase.

Variables globales d’état

Voici un exemple banal pour quelques variables globales pour un jeu.

Fichier : GlobalGameState.java

int lives = 3;
int xp = 0;
boolean hasWeapon = true;
boolean hasHorse = true;

void diedInBattle() {
    xp += 5;
    lives--;
    hasWeapon = false;
    System.out.println("Vous ĂȘtes mort au combat et avez perdu votre arme!");
}

void diedWhileRiding() {
    xp += 2;
    lives--;
    hasHorse = false;
    System.out.println("Vous ĂȘtes mort en tombant de votre cheval!");
}

void showStats() {
    System.out.println("Vies : " + lives);
    System.out.println("Points d'expérience : " + xp);
    System.out.println("Vous avez une arme : " + hasWeapon);
    System.out.println("Vous avez un cheval : " + hasHorse);
}

void main() {
    showStats();
    diedInBattle();
    showStats();
    diedWhileRiding();
    showStats();
}

Priorité de nom selon la portée

Une des caractĂ©ristiques d’un programme avec diffĂ©rentes portĂ©es de variables est que le mĂȘme nom peut ĂȘtre dĂ©clarĂ© Ă  plusieurs endroits sans conflits en autant que leurs portĂ©es soient diffĂ©rentes.

S’il y a deux variables du mĂȘme type et du mĂȘme nom, mais une est globale est l’autre est locale, la version locale sera toujours utilisĂ©e en prioritĂ©.

Voici quelques exemples courts pour illustrer la chose.

/* Globale versus locale */

String name = "Steve";

void main() {
    String name = "Angelica";
    name = "Naomi";
    System.out.println(name);
}
Quelle variable est modifiée à la ligne 7? locale ou globale?

La variable locale, déclarée à la ligne 6, car elle a priorité sur la variable globale.

Quel nom s'affiche? Naomi ou Steve?

Naomi. La variable locale dans main masque la variable globale (qui n’est plus accessible dans main à cause de ce masquage).

/* Locale versus locale */

void main() {
    String name = "Angelica";
    System.out.println(rename(name));
    System.out.println(name);
}

String rename(String name) {
    name = "Demonica";
    return name;
}
Qu'est-ce qui s'affiche Ă  la ligne 5?

Demonica. C’est la valeur de retour de rename.

Qu'est-ce qui s'affiche Ă  la ligne 6?

Angelica. Le paramĂštre name est locale Ă  la mĂ©thode rename. Cette variable rĂ©coit la valeur de la variable dans main comme argument. Par contre, ce paramĂštre (locale Ă  rename) n’existe plus aprĂšs le retour de la mĂ©thode. rename retourne la nouvelle valeur, mais la variable name dans main ne la reçoit pas. La valeur de retour passe plutĂŽt directement dans un print. Alors la variable locale dans main n’a subit aucune modification du dĂ©but jusqu’à la fin.

Quelques exemples d’erreur (de conflit) de portĂ©e

Variable inaccessible

void isolated() {
    int answer = 7;
}

void main() {
    System.out.println(answer); // erreur "cannot find symbol"
}

Ici, answer est locale Ă  la mĂ©thode isolated, alors il n’est pas visible dans main, contrairement Ă  si cette variable avait Ă©tĂ© dĂ©clarĂ©e globalement, Ă  l’extĂ©rieure d’une mĂ©thode.

int answer = 7; // déclaration globale

void main() {
    System.out.println(answer); // answer est visible
}

Déclaration dupliquée

String thing(String t) {
    String t = t + "2"; // erreur "duplicate local variable"
    return t;
}

void main() {
    System.out.println(thing("thing"));
}

Ici, dans la mĂ©thode thing on dĂ©clare dĂ©jĂ  un paramĂštre String t. Alors quand on dĂ©clare une autre variable locale du mĂȘme nom Ă  la ligne 2, Java nous indique une erreur. Si l’intention Ă©tait de simplement modifier la valeur reçue dans le paramĂštre alors on peut réécrire la ligne 2 comme ceci :

String thing(String t) {
    t = t + "2"; // référence valide au paramÚtre t déjà déclarée
    return t;
}

void main() {
    System.out.println(thing("thing"));
}

La diffĂ©rence est que la ligne 2 n’est plus une dĂ©claration d’une nouvelle variable. Elle est dĂ©sormais une rĂ©fĂ©rence Ă  la variable dĂ©jĂ  dĂ©clarĂ©e dans la liste de paramĂštres.

Exercices

📚 Tester la comprĂ©hension

Faire les deux questions de choix multiples au dĂ©but de la page ici pour vĂ©rifier votre comprĂ©hension de la portĂ©e des variables. Ne faites pas les problĂšmes de programmation car ils intĂšgrent d’autres concepts que nous ne verrons pas dans ce cours.

La raison que les exercices de programmation sur la page sont dĂ©conseillĂ©s est qu’ils sollicitent des concepts de la programmation orientĂ©e objet que nous n’avons pas appris. Ce sont plutĂŽt des concepts du cours ICS4U.

Je vous suggÚre plutÎt les exercices pratiques suivants qui sont mieux adaptés à notre contexte.

đŸ› ïž Pratique

Travaillez dans le répertoire GitHub partagé par votre enseignant pour la pratique et les exercices.

Cette pratique vise Ă  dĂ©montrer les diffĂ©rences entre une approche de variables globales et une approche de variables locales dans un cas oĂč les deux sont des choix valides.

Le contexte est l’utilisation d’un symbole spĂ©cifique comme indicateur d’invite de rĂ©ponses chaque fois qu’on pose une question Ă  l’utilisateur. On veut que ce symbole soit facile Ă  changer si on veut utiliser un autre symbole. On veut aussi que le symbole soit utilisĂ© dans plusieurs mĂ©thodes.

Approche avec variable globale

Avec cette approche, on dĂ©clare une variable globale pour le symbole. Par la suite nous concatenons ce symbole Ă  la fin de chaque question posĂ©e Ă  l’utilisateur. Pour montrer l’utilitĂ© Ă  travers plusieurs mĂ©thodes, chaque question se trouve dans sa propre mĂ©thode.

Le Scanner de la console est également déclaré comme variable globale pour un accÚs direct pour toutes les méthodes.

diagramme de méthodes pour var globale

Fichier GlobalVar.java

import java.util.*;

final Scanner CONSOLE = new Scanner(System.in);
final String PROMPT = " > ";

void main() {
    String name = getName();
    int age = getAge();
    System.out.println("Votre nom est " + name + " et vous avez " + age + " ans.");
}

String getName(){
    System.out.print("Quel est votre prénom?" + PROMPT);
    String name = CONSOLE.next();
    CONSOLE.nextLine(); //vider le reste de la ligne
    return name;
}

int getAge() {
    System.out.print("Quel est votre Ăąge?" + PROMPT);
    int age = CONSOLE.nextInt();
    CONSOLE.nextLine();
    return age;
}
  1. Copiez ce code dans un fichier GlobalVar.java et l’exĂ©cuter pour voir comment il fonctionne.
  2. Ajouter un commentaire aprĂšs chaque dĂ©claration de variable pour indiquer s’il s’agit d’une variable locale ou globale. Les variables sont CONSOLE, PROMPT; name, age; name; age.
  3. Modifiez la valeur de PROMPT pour utiliser un autre symbole que >. Lancez le programme de nouveau pour voir le changement.
  4. Renommez les variables dans main et lancez le programme pour voir si ça change le fonctionnement. Selon vos observations, est-ce que la variable nommĂ©e name dans main est le mĂȘme objet en mĂ©moire que la variable nommĂ©e name dans getName?

Approche avec variables locales seulement

Avec cette approche, les variables Ă  partager sont dĂ©clarĂ©es localement dans main et partagĂ© via le mĂ©canisme argument-paramĂštre. De plus, au lieu de crĂ©er une variable pour le symbole prompt, on crĂ©e une mĂ©thode. Parce que chaque mĂ©thode est visible par les autres dans le fichier, toutes les autres mĂ©thodes peuvent l’appeler.

diagramme de dépendances pour var locale

Fichier LocalVar.java

import java.util.*;

void main() {
    Scanner console = new Scanner(System.in);
    String name = getName(console);
    int age = getAge(console);
    System.out.println("Votre nom est " + name + " et vous avez " + age + " ans.");
}

void prompt(String question) {
    System.out.print(question + " > ");
}

String getName(Scanner in) {
    prompt("Quel est votre nom?");
    String name = in.next();
    in.nextLine(); // vider le reste de la ligne
    return name;
}

int getAge(Scanner in) {
    prompt("Quel est votre Ăąge?");
    int age = in.nextInt();
    in.nextLine();
    return age;
}
  1. Copiez ce code dans un fichier LocalVar.java et l’exĂ©cuter pour voir comment il fonctionne. Confirmez qu’il fonctionne - du point de vue de l’utilisateur - de maniĂšre identique Ă  GlobalVar.
  2. Modifiez le programme pour changer le symbole d’invite de commande dans prompt(). Est-ce que c’était plus facile ou difficile ou Ă©quivalent Ă  ce qu’il fallait faire la derniĂšre fois?
  3. ÉnumĂ©rer les Ă©lĂ©ments que vous avez prĂ©fĂ©rez de la version GlobalVar et les Ă©lĂ©ments que vous avez prĂ©fĂ©rez dans LocalVar. ÉnumĂ©rer aussi des Ă©lĂ©ments que vous n’avez pas aimĂ© dans les deux versions. Ajoutez ces listes dans un commentaire d’en-tĂȘte dans un troisiĂšme fichier PreferredVar.java. Combiner vos Ă©lĂ©ments prĂ©fĂ©rĂ©s de chaque version dans cette troisiĂšme version ou apporter des modifications pour crĂ©er votre propre approche. RĂ©soudre les erreurs de fusion et lancer ce troisiĂšme programme pour vĂ©rifier qu’à la console l’expĂ©rience reste identique pour l’utilisateur malgrĂ© les changements.

  1. Il y a des nuances Ă  “partout dans un programme”, mais ces nuances ne sont pas importants dans ce cours. Pour votre curiositĂ©, voici quand mĂȘme un bref survol. Nos programmes tiennent dans un seul fichier .java, alors partout dans le fichier serait plus apte. Les fichiers .java reprĂ©sentent implicitement une classe Java. Alors partout dans la classe serait aussi plus prĂ©cis. Mais les variables Java peuvent ĂȘtre qualifiĂ©es de publiques ou privĂ©es. Les variables privĂ©es sont seulement “globales” dans la classe, tandis que les variables publiques sont “globales” pour tout un package de classes (Ă  l’extĂ©rieur de la classe elle-mĂȘme). Ce concept s’étend Ă  la portĂ©e des classes (publiques ou privĂ©es) dans les packages et la portĂ©e des packages (exportĂ©es ou non) dans des modules. ↩

© 2022-2025 David Crowley