Gestion des cartes

tCHu – étape 3

1. Introduction

Le but principal de cette étape est d'écrire les classes permettant de gérer les cartes et les billets au cours d'une partie de tCHu. Son but secondaire est d'écrire la classe permettant de générer les messages informant les joueurs des actions effectuées par leur adversaire, et de l'avancement de la partie.

2. Concepts

Afin de réaliser cette étape, il est important de comprendre en détail comment se déroule une partie de tCHu.

Au début de l'une d'entre elles, le joueur qui commencera est désigné aléatoirement.

Cela fait, chaque joueur reçoit 40 wagons de la couleur qui l'identifie — bleu clair pour le joueur 1, rose pour le joueur 2. Ces wagons, à ne pas confondre avec les cartes wagon, sont utilisés pour prendre possession des routes, comme décrit plus bas.

Ensuite, les 110 cartes wagon/locomotive du jeu sont mélangées et chaque joueur en reçoit 4, qu'il prend en main sans les montrer à son adversaire. De plus, 5 cartes sont placées au bord du plateau de jeu, face visible. Finalement, un tas est formé avec les cartes restantes, face cachée, qui constitue la pioche de cartes.

En plus des 4 cartes wagon/locomotive, chaque joueur reçoit également 5 billets choisis aléatoirement. Il doit en garder au moins 3, les billets non désirés étant définitivement défaussés. Un tas est formé avec les billets non distribués, face cachée, qui constitue la pioche de billets.

Lorsque les deux joueurs ont choisi les billets qu'ils désirent garder, le premier tour débute. En commençant par le joueur désigné aléatoirement, chacun effectue alors à tour de rôle l'une des trois actions suivantes :

  1. tirer deux cartes wagon/locomotive, ou
  2. tirer trois billets, ou
  3. s'emparer d'une route.

Ces trois actions sont décrites en détail ci-dessous.

2.1. Tirage de cartes

Lorsqu'un joueur décide de tirer deux cartes wagon/locomotive, chacune d'entre elles peut être soit :

  • l'une des cinq cartes disposées face visible au bord du plateau,
  • la carte au sommet de la pioche, dont la face est cachée.

Lorsque le joueur choisit l'une des cinq cartes dont la face est visible, la carte au sommet de la pioche est retournée et placée à l'emplacement devenu vide. De la sorte, il y a toujours exactement cinq cartes dont la face est visible1.

Comme décrit plus bas, lorsqu'un joueur s'empare d'une route, il joue un certain nombre des cartes qu'il a en main. Ces cartes sont placées dans ce que l'on nomme la défausse (discards), qui est un tas de cartes distinct de la pioche. Lorsque cette dernière est vide, les cartes de la défausse sont mélangées afin de constituer la nouvelle pioche.

Le tirage de cartes n'est autorisé que lorsque le nombre total de cartes disponibles dans la pioche et la défausse est d'au moins cinq. Cette règle, spécifique à tCHu, simplifie la mise en œuvre sans pour autant dénaturer le jeu.

2.2. Tirage de billets

Lorsqu'un joueur décide de tirer des billets, il tire les trois premiers de la pioche, puis en garde au moins un. Les billets qu'il ne désire pas garder sont définitivement défaussés. Il est donc possible — et même fréquent — que la pioche de billets soit vide avant la fin de la partie, rendant impossible le tirage de billets.

Sachant qu'il y a un total de 46 billets en jeu, et que 10 d'entre eux (2×5) sont distribués en début de partie, la pioche en contient initialement 36. Ces billets étant ensuite tirés par groupe de 3, il est toujours possible de tirer exactement 3 billets tant que la pioche n'est pas vide — car 36 est divisible par 3.

2.3. Prise de possession d'une route

Lorsqu'un joueur décide de s'emparer d'une route, il doit suivre les règles données à l'étape précédente.

2.4. Informations concernant le déroulement

Au cours de la partie, chaque joueur doit être informé des actions effectuées par son adversaire. Par exemple, si un joueur s'empare d'une route en surface, son adversaire doit savoir avec quelles cartes il s'en est emparé.

Dans la version « physique » des Aventuriers du Rail, les joueurs sont côte à côte et peuvent donc simplement observer les actions de l'adversaire. Dans une partie de tCHu, les joueurs sont chacun devant leur propre ordinateur, et ne se trouvent pas forcément à proximité l'un de l'autre. Il faut donc trouver un moyen de communiquer à chaque joueur les informations lui permettant de suivre le déroulement de la partie.

Une manière de faire serait de simuler à l'écran les actions du jeu original. Par exemple, les cartes utilisées par un joueur pour s'emparer d'une route pourraient être présentées sur le plateau durant un certain temps.

Cette solution est malheureusement un peu trop complexe à mettre en œuvre dans le cadre de ce projet, et nous en avons donc retenu une autre, qui consiste à informer textuellement les joueurs du déroulement de la partie. Par exemple, si une joueuse nommée Ada s'empare de la route Lausanne - Neuchâtel au moyen de 4 cartes vertes, le message textuel suivant s'affiche sur l'écran de tous les joueurs :

Ada a pris possession de la route Lausanne - Neuchâtel au moyen de 4 vertes.

3. Mise en œuvre Java

Avant de commencer la mise en œuvre de cette étape, vous devez une fois encore importer dans votre projet le contenu d'une archive Zip que nous vous fournissons et qui contient la classe StringsFr, qui définit les chaînes de caractères qui apparaissent dans l'interface (en français). Elle est décrite plus bas, juste avant la classe Info qui l'utilise.

3.1. Classe Deck

La classe Deck du paquetage ch.epfl.tchu.game, publique, finale et immuable, représente un tas de cartes. Notez qu'ici le mot « carte » désigne n'importe quel type de carte, et pas seulement des cartes wagon/locomotive. Ainsi, dans ce projet, la classe Deck sera utilisée aussi bien pour représenter la pioche des cartes wagon/locomotive que pour représenter la pioche des billets.

La classe Deck est générique, car le type exact des cartes contenues dans le tas qu'elle représente n'est pas fixé d'avance. Son paramètre de type, nommé C, représente le type des cartes du tas. Étant donné que certaines méthodes de Deck retournent un multiensemble de cartes, il est nécessaire que ce paramètre de type soit borné de la même manière que celui de SortedBag. La classe Deck est donc définie ainsi :

public final class Deck<C extends Comparable<C>> {
  // … corps de la classe
}

La classe Deck n'offre pas de constructeur public, mais la méthode de construction publique et statique (et générique) suivante :

  • <C extends Comparable<C>> Deck<C> of(SortedBag<C> cards, Random rng), qui retourne un tas de cartes ayant les mêmes cartes que le multiensemble cards, mélangées au moyen du générateur de nombres aléatoires rng (voir les conseils de programmation plus bas).

Bien entendu, cette méthode de construction utilise le constructeur de Deck pour créer l'instance qu'elle retourne, mais ce constructeur est privé.

En dehors de cette méthode de construction statique, la classe Deck possède également les méthodes publiques suivantes :

  • int size(), qui retourne la taille du tas, c-à-d le nombre de cartes qu'il contient,
  • boolean isEmpty(), qui retourne vrai ssi le tas est vide,
  • C topCard(), qui retourne la carte au sommet du tas, ou lève IllegalArgumentException si le tas est vide,
  • Deck<C> withoutTopCard(), qui retourne un tas identique au récepteur (this) mais sans la carte au sommet, ou lève IllegalArgumentException si le tas est vide,
  • SortedBag<C> topCards(int count), qui retourne un multiensemble contenant les count cartes se trouvant au sommet du tas ; lève IllegalArgumentException si count n'est pas compris entre 0 (inclus) et la taille du tas (incluse),
  • Deck<C> withoutTopCards(int count), qui retourne un tas identique au récepteur (this) mais sans les count cartes du sommet, ou lève IllegalArgumentException si count n'est pas compris entre 0 (inclus) et la taille du tas (incluse).

3.1.1. Conseils de programmation

Prenez bien garde au fait que la classe Deck est immuable ! En conséquence, aucune méthode ne doit modifier le contenu du tas.

Par exemple, la méthode topCard retourne la carte au sommet du tas, sans pour autant l'en supprimer. Le seul moyen de « supprimer » la carte au sommet du tas consiste à utiliser la méthode withoutTopCard, qui retourne un nouveau tas, identique à celui auquel on l'applique, mais sans la carte au sommet.

L'utilisation de ces deux méthodes peut être illustrée au moyen de l'extrait de programme suivant, qui affiche à l'écran toutes les cartes d'un tas de 5 « cartes », ici de simples chaînes de caractères :

1: SortedBag<String> cards =
2:   SortedBag.of(2, "as de pique", 3, "dame de cœur");
3: Deck<String> deck = Deck.of(cards, new Random());
4: 
5: while (!deck.isEmpty()) {
6:   String topCard = deck.topCard();
7:   deck = deck.withoutTopCard();
8:   System.out.println(topCard);
9: }

Notez bien que ce programme ne modifie pas le tas de cartes créé à la ligne 3 ! Au lieu de cela, à chaque itération de la boucle, un nouveau tas est recréé au moyen de la méthode withoutTopCard et stocké à la place de l'ancien dans la variable deck, à la ligne 7.

Il est fortement conseillé de stocker les cartes du tas dans une liste de type List<C>. Pour transformer le multiensemble de cartes passé à la méthode of en liste, vous pouvez utiliser la méthode toList de SortedBag. Pour effectuer la transformation inverse, c-à-d créer un multiensemble à partir d'une liste — ce que doit faire la méthode topCards —, vous pouvez utiliser le bâtisseur SortedBag.Builder. Finalement, pour obtenir une sous-liste d'une liste existante, vous pouvez utiliser la méthode subList de List, dont l'utilisation est illustrée par l'exemple ci-dessous :

List l1 = List.of("zéro", "un", "deux", "trois", "quatre");
List l2 = l1.subList(1, 3);

// s est égale à "[un, deux]"
String s = l2.toString();

Notez bien que l'élément d'index 3 n'est pas inclus dans la liste l2, car le second index passé à subList est exclu du résultat.

Pour mélanger les cartes du tas, vous pouvez utiliser la méthode statique shuffle de Collections, qui mélange les éléments d'une liste. Pour ce faire, cette méthode utilise un générateur de nombres aléatoires, représenté en Java par la classe Random. Vous n'avez pour l'instant pas besoin de savoir quoi que ce soit à propos de cette classe, étant donné que la méthode of doit simplement passer l'instance de Random qu'elle reçoit à la méthode shuffle.

Faites bien attention à utiliser la bonne version de la méthode shuffle, à savoir celle qui prend deux arguments, dont justement une valeur de type Random. Utiliser l'autre serait incorrect car nous désirons avoir le contrôle de l'instance de Random utilisée.

3.2. Classe PublicCardState

La classe PublicCardState du paquetage ch.epfl.tchu.game, publique et immuable, représente (une partie de) l'état des cartes wagon/locomotive qui ne sont pas en main des joueurs, à savoir :

  1. les 5 cartes disposées face visible à côté du plateau,
  2. la pioche,
  3. la défausse.

Comme son nom l'indique, PublicCardState ne représente en fait que ce que nous appellerons la partie publique de cet état, par opposition à la partie privée représentée par la classe CardState décrite plus bas2.

Par « partie publique d'un état », on entend la partie de cet état qui est connue de tous les joueurs. Par exemple, tous les joueurs connaissent la taille de la pioche de cartes, mais pas son contenu exact. Dès lors, la taille de la pioche appartient à la partie publique de l'état des cartes, alors que son contenu exact appartient à la partie privée.

Nous ferons cette distinction entre partie publique et privée d'un état à plusieurs reprises dans ce projet. Ces deux parties seront systématiquement représentées par deux classes :

  1. une classe représentant la partie publique, dont le nom commence par Public (PublicCardState ici),
  2. une classe représentant la totalité de l'état, qui hérite de celle représentant la partie publique et lui ajoute la partie privée ; son nom est celui de sa classe mère amputé du préfixe Public (CardState ici).

L'intérêt de découper ainsi l'état du jeu en deux parties ne deviendra clair que bien plus tard dans le projet, lorsque nous écrirons le code de communication réseau.

La classe PublicCardState offre un unique constructeur public :

  • PublicCardState(List<Card> faceUpCards, int deckSize, int discardsSize), qui construit un état public des cartes dans lequel les cartes face visible sont celles données, la pioche contient deckSize cartes et la défausse en contient discardsSize ; lève IllegalArgumentException si faceUpCards ne contient pas le bon nombre d'éléments (5), ou si la taille de la pioche ou de la défausse sont négatives (< 0).

De plus, la classe PublicCardState offre les méthodes publiques suivantes :

  • int totalSize(), qui retourne le nombre total de cartes qui ne sont pas en main des joueurs, à savoir les 5 dont la face est visible, celles de la pioche et celles de la défausse,
  • List<Card> faceUpCards(), qui retourne les 5 cartes face visible, sous la forme d'une liste comportant exactement 5 éléments,
  • Card faceUpCard(int slot), qui retourne la carte face visible à l'index donné, ou lève IndexOutOfBoundsException (!) si cet index n'est pas compris entre 0 (inclus) et 5 (exclus),
  • int deckSize(), qui retourne la taille de la pioche,
  • boolean isDeckEmpty(), qui retourne vrai ssi la pioche est vide,
  • int discardsSize(), qui retourne la taille de la défausse.

3.2.1. Conseils de programmation

Pour valider l'index passé à faceUpCard, vous pouvez utiliser la méthode checkIndex de Objects, qui prend en argument un index et une taille et lève IndexOutOfBoundsException si l'index n'est pas compris entre 0 (inclus) et la taille (exclue). Sinon, checkIndex retourne simplement l'index qu'on lui a passé.

3.3. Classe CardState

La classe CardState du paquetage ch.epfl.tchu.game, publique, finale et immuable, représente l'état des cartes wagon/locomotive qui ne sont pas en main des joueurs. Comme expliqué plus haut, elle hérite de PublicCardState et lui ajoute les éléments privés de l'état, ainsi que les méthodes correspondantes.

CardState n'offre pas de constructeur public, mais une méthode de construction publique et statique :

  • CardState of(Deck<Card> deck), qui retourne un état dans lequel les 5 cartes disposées faces visibles sont les 5 premières du tas donné, la pioche est constituée des cartes du tas restantes, et la défausse est vide ; lève IllegalArgumentException si le tas donné contient moins de 5 cartes.

En plus de cette méthode de construction publique, la classe CardState offre les méthodes publiques suivantes :

  • CardState withDrawnFaceUpCard(int slot), qui retourne un ensemble de cartes identique au récepteur (this), si ce n'est que la carte face visible d'index slot a été remplacée par celle se trouvant au sommet de la pioche, qui en est du même coup retirée ; lève IndexOutOfBoundsException (!) si l'index donné n'est pas compris entre 0 (inclus) et 5 (exclus), ou IllegalArgumentException si la pioche est vide,
  • Card topDeckCard(), qui retourne la carte se trouvant au sommet de la pioche, ou lève IllegalArgumentException si la pioche est vide,
  • CardState withoutTopDeckCard(), qui retourne un ensemble de cartes identique au récepteur (this), mais sans la carte se trouvant au sommet de la pioche ; lève IllegalArgumentException si la pioche est vide,
  • CardState withDeckRecreatedFromDiscards(Random rng), qui retourne un ensemble de cartes identique au récepteur (this), si ce n'est que les cartes de la défausse ont été mélangées au moyen du générateur aléatoire donné afin de constituer la nouvelle pioche ; lève IllegalArgumentException si la pioche du récepteur n'est pas vide,
  • CardState withMoreDiscardedCards(SortedBag<Card> additionalDiscards), qui retourne un ensemble de cartes identique au récepteur (this), mais avec les cartes données ajoutées à la défausse.

3.3.1. Conseils de programmation

La pioche est naturellement représentée par un tas de cartes de type Deck<Card>. La défausse est quant à elle naturellement représentée par un multiensemble trié de cartes, de type SortedBag<Card>, car l'ordre des cartes de la défausse n'importe pas.

N'oubliez pas l'existence de la classe Constants, qui contient plusieurs définitions utiles à l'écriture des classes de cette étape. En particulier, notez que si vous désirez parcourir les index des cartes face visible, vous pouvez parcourir les éléments de FACE_UP_CARD_SLOTS au moyen d'une boucle for-each :

for (int slot: FACE_UP_CARD_SLOTS) { /* … */ }

ce qui peut être plus lisible d'une boucle for normale.

3.4. Classe StringsFr (fournie)

La classe StringsFr, du paquetage ch.epfl.tchu.gui, non instanciable, contient la totalité des chaînes de caractères utilisées dans le jeu, rédigées en français. Regrouper ainsi les chaînes dans une seule classe permet de simplifier la traduction de l'interface graphique.

Notez bien que cette classe appartient au sous-paquetage gui, destiné à contenir les entités liées à l'interface utilisateur graphique, GUI étant l'acronyme de graphical user interface. La quasi-totalité des classes de ce paquetage ne sera écrite qu'à la fin du projet, la seule exception étant la classe Info décrite à la section suivante.

Avant de continuer, lisez la classe StringsFr et trouvez la chaîne que vous devrez utiliser pour produire le message d'information mentionné à la fin de la §2.4. Notez que cette chaîne, comme beaucoup d'autre, contient plusieurs occurrences de la chaîne %s, ce qui indique qu'il s'agit d'une chaîne de formattage à passer à la méthode format de String décrite à l'étape 1. (Plusieurs chaînes contiennent aussi la séquence d'échappement \n, qui représente un retour à la ligne, comme vous le savez peut-être.)

En plus des chaînes de caractères, la classe StringsFr offre une méthode statique nommée plural qui retourne une chaîne vide si l'entier qu'on lui passe vaut 1 en valeur absolue, et la chaîne composée du seul caractère s sinon. Cette méthode est utile pour ajouter un « s » à la fin des mots pour indiquer le pluriel.

3.5. Classe Info

La classe Info, du paquetage ch.epfl.tchu.gui, publique, finale et immuable, permet de générer les textes décrivant le déroulement de la partie. La plupart de ces messages décrivent les actions d'un joueur donné, dont le nom est passé en argument au constructeur :

  • Info(String playerName), qui construit un générateur de messages liés au joueur ayant le nom donné.

Toutes les méthodes de la classe Info utilisent une ou plusieurs chaînes de la classe StringsFr. Pour faciliter votre travail, le nom de ces chaînes est donné entre parenthèses à la fin de la description de chaque méthode.

Les méthodes publiques et statiques de la classe Info permettent de générer des (bouts de) messages qui ne sont pas spécifiques à un joueur donné. Ces méthodes sont :

  • String cardName(Card card, int count), qui retourne le nom (français) de la carte donnée, au singulier ssi la valeur absolue du second argument vaut 1 (utilise BLACK_CARD, BLUE_CARD, etc.),
  • String draw(List<String> playerNames, int points), qui retourne le message déclarant que les joueurs, dont les noms sont ceux donnés, ont terminé la partie ex æqo en ayant chacun remporté les points donnés (utilise DRAW).

En plus de ces méthodes statiques, la classe Info offre des méthodes publiques permettant de générer des (bouts de) messages spécifiques au joueur dont le nom a été passé au constructeur, désigné par « le joueur » ci-dessous. Il s'agit de :

  • String willPlayFirst(), qui retourne le message déclarant que le joueur jouera en premier (WILL_PLAY_FIRST),
  • String keptTickets(int count), qui retourne le message déclarant que le joueur a gardé le nombre de billets donné (KEPT_N_TICKETS),
  • String canPlay(), qui retourne le message déclarant que le joueur peut jouer (CAN_PLAY),
  • String drewTickets(int count), qui retourne le message déclarant que le joueur a tiré le nombre donné de billets (DREW_TICKETS),
  • String drewBlindCard(), qui retourne le message déclarant que le joueur a tiré une carte « à l'aveugle », c-à-d du sommet de la pioche (DREW_BLIND_CARD),
  • String drewVisibleCard(Card card), qui retourne le message déclarant que le joueur a tiré la carte disposée face visible donnée (DREW_VISIBLE_CARD),
  • String claimedRoute(Route route, SortedBag<Card> cards), qui retourne le message déclarant que le joueur s'est emparé de la route donnée au moyen des cartes données (CLAIMED_ROUTE),
  • String attemptsTunnelClaim(Route route, SortedBag<Card> initialCards), qui retourne le message déclarant que le joueur désire s'emparer de la route en tunnel donnée en utilisant initialement les cartes données (ATTEMPTS_TUNNEL_CLAIM),
  • String drewAdditionalCards(SortedBag<Card> drawnCards, int additionalCost), qui retourne le message déclarant que le joueur a tiré les trois cartes additionnelles données, et qu'elles impliquent un coût additionel du nombre de cartes donné (ADDITIONAL_CARDS_ARE, NO_ADDITIONAL_COST, SOME_ADDITIONAL_COST),
  • String didNotClaimRoute(Route route), qui retourne le message déclarant que le joueur n'a pas pu (ou voulu) s'emparer du tunnel donné (DID_NOT_CLAIM_ROUTE),
  • String lastTurnBegins(int carCount), qui retourne le message déclarant que le joueur n'a plus que le nombre donné (et inférieur ou égale à 2) de wagons, et que le dernier tour commence donc (LAST_TURN_BEGINS),
  • String getsLongestTrailBonus(Trail longestTrail), qui retourne le message déclarant que le joueur obtient le bonus de fin de partie grâce au chemin donné, qui est le plus long, ou l'un des plus longs (GETS_BONUS, EN_DASH_SEPARATOR pour séparer les deux gares du chemin),
  • String won(int points, int loserPoints), qui retourne le message déclarant que le joueur remporte la partie avec le nombre de points donnés, son adversaire n'en ayant obtenu que loserPoints.

Plusieurs des messages générés par les méthodes ci-dessus incluent un nom de route, qui doit obligatoirement être composé du nom de la première gare, le séparateur donné par StringsFr.EN_DASH_SEPARATOR, puis le nom de la seconde gare. Par exemple, le nom de la route Lausanne - Neuchâtel est :

Lausanne – Neuchâtel

De même, plusieurs messages incluent la description d'un ensemble de cartes, qui doit obligatoirement être formé de la manière suivante :

  • chaque nom de carte est précédé de sa multiplicité,
  • chaque nom de carte est accordé en nombre à sa multiplicité (au singulier si elle vaut 1, au pluriel sinon),
  • les paires multiplicité/nom de carte sont ordonnées selon l'ordre du type Card,
  • les paires multiplicité/nom de carte sont séparées les unes des autres par une virgule et un espace, sauf la dernière paire, qui est séparée des précédentes par le texte de StringFr.AND_SEPARATOR.

Par exemple, l'ensemble de cartes formé de deux cartes oranges, d'une carte locomotive et d'une carte verte a pour description la chaîne :

1 verte, 2 oranges et 1 locomotive

3.5.1. Conseils de programmation

Il est conseillé d'écrire des méthodes statiques et privées permettant d'obtenir le nom d'une route ou la description d'un ensemble de cartes.

Dans la seconde de ces méthodes, pour parcourir les différentes cartes d'un multiensemble dans l'ordre et connaître la multiplicité de chacune, vous pouvez utiliser les méthode toSet et countOf de SortedBag. Par exemple, l'extrait de programme suivant affiche à l'écran les cartes d'un multiensemble de cartes :

SortedBag<Card> cards = …;
for (Card c: cards.toSet()) {
  int n = cards.countOf(c);
  System.out.println(c + " " + n);
}

Si cards est le multiensemble décrit plus haut, cette boucle affiche les trois lignes suivantes à l'écran :

GREEN 1
ORANGE 2
LOCOMOTIVE 1

3.6. Tests

Comme pour l'étape précédente, nous ne vous fournissons plus de tests mais un fichier de vérification de signatures contenu dans une archive Zip à importer dans votre projet.

4. Résumé

Pour cette étape, vous devez :

  • écrire les classes Deck, PublicCardState, CardState et Info selon les indications données ci-dessus,
  • tester votre code,
  • documenter la totalité des entités publiques que vous avez définies,
  • rendre votre code au plus tard le 12 mars 2021 à 17h00, via le système de rendu.

Ce rendu est un rendu testé, auquel 20 points sont attribués, au prorata des tests unitaires passés avec succès.

N'attendez surtout pas le dernier moment pour effectuer votre rendu, car vous n'êtes pas à l'abri d'imprévus. Souvenez-vous qu'aucun retard, aussi insignifiant soit-il, ne sera toléré !

Notes de bas de page

1

Dans Les Aventuriers du Rail, une règle précise que si au moins trois des cinq cartes dont la face est visible sont des locomotives, alors les cinq cartes doivent être défaussées et remplacées par les cinq se trouvant au sommet de la pioche. Dans un soucis de simplicité, et en raison du faible impact de cette règle sur le jeu, elle n'existe pas dans tCHu.

2

Notez que les termes « public » et « privé » utilisés ici n'ont aucun lien avec les modificateurs public et private de Java.