Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Algorithmique
  3. Les relations entre les classes
Extrait - Algorithmique Des bases à la programmation orientée objet en Java (avec exercices et corrigés) (2e édition)
Extraits du livre
Algorithmique Des bases à la programmation orientée objet en Java (avec exercices et corrigés) (2e édition) Revenir à la page d'achat du livre

Les relations entre les classes

Présentation

Dans le chapitre précédent, les classes créées n’ont aucune relation avec d’autres classes. Ce chapitre présente comment les classes peuvent être mises en relation les unes avec les autres. Il existe trois types de relations entre deux classes. Tout d’abord, il y a l’utilisation d’une classe par une autre. Ensuite, il y a l’association d’une classe avec une autre. Enfin, il y a l’héritage d’une classe par une autre.

L’utilisation d’une classe par une autre

Une classe utilise une autre classe dans différentes situations :

  • Lorsque la première possède une méthode prenant en paramètre une instance de l’autre.

  • Lorsqu’une des méthodes de la première retourne une instance de la seconde.

  • Lorsque la première fait appel à une méthode, à une constante ou à un constructeur de la seconde.

En résumé, dès que le nom d’une classe apparaît dans une autre classe, cette dernière utilise la première.

Par exemple, la classe GrilleDeJeu présentée ci-dessous utilise la classe Bateau :

Classe GrilleDeJeu  
Constante BOOM : entier <- 2  
Constante INCONNU : entier <- 3  
Constante PLOUF : entier <- 4  
Constante LARGEUR : entier <- 10  
Constante HAUTEUR : entier <- 10  
Attribut plateauDeJeu : entier[HAUTEUR][LARGEUR]  
  
Méthodes  
  
Constructeur()  
Variable i, j : entier  
Début  
 Pour j <- 0 à HAUTEUR - 1  
   Pour i <- 0 à LARGEUR - 1  
     instance.plateauDeJeu[j][i] <- INCONNU  
   FPour  
 FPour  
Fin  
  
Procédure afficher()  
Variable i, j : entier  
Début  
 Pour j <-...

Les associations

L’association d’une classe vis-à-vis d’une autre est une relation plus forte que la simple utilisation. Non seulement la classe utilise l’autre classe, mais elle stocke également une ou plusieurs instances de celle-ci au sein d’un ou de plusieurs de ses attributs. Dans le chapitre précédent, des variables de type d’une classe et référençant une instance de celle-ci étaient utilisées. Il y avait également des classes ayant des attributs de type entier, réel, booléen, caractère ou texte, ou bien encore un tableau contenant des valeurs de l’un de ces types. Dans ce paragraphe est présentée la création d’attributs de type d’une classe ou d’un tableau contenant des instances de classe : cela correspondant à la déclaration d’une association.

L’exemple suivant illustre ce concept d’association :

Dans la classe Joueur, il y a six attributs. Trois d’entre eux utilisent des types qui ne sont pas des classes (un de type texte et deux de type entier). Les trois autres attributs ont pour type des classes, ce sont donc trois associations.

Classe Joueur  
  
Attribut nom : texte  
Attribut champDeTir : GrilleDeJeu <- nouveau GrilleDeJeu()  
Attribut bateaux : Bateau[5]  
Attribut nbBateaux : entier <- 0  
Attribut nbBateauxCoules : entier <- 0  
Attribut adversaire : Joueur  
  
...  
  
FClasse 

Tout d’abord, l’attribut champDeTir est de type Grille. À une instance de Joueur est donc associée une instance de GrilleDeJeu, c’est-à-dire la zone dans laquelle il va effectuer ses tirs avec pour objectif de couler les bateaux de son adversaire. Cette association ne va que dans un seul sens : une instance de Joueur connaît sa grille de jeu grâce à cet attribut, mais par contre, la grille de jeu ne sait pas à quel joueur elle est associée. Cette association est dite navigable que dans un seul sens (c’est bien le terme officiel, ce n’est pas pour rester dans la thématique de la bataille navale…). C’est donc une association unidirectionnelle.

Ensuite, l’attribut bateaux est un tableau dont les éléments...

L’héritage

1. La notion d’héritage

La troisième relation qu’une classe peut posséder vis-à-vis d’une autre est l’héritage. L’objectif de l’héritage est de définir une classe non pas à partir de zéro, mais à partir d’une autre classe. La classe servant de base est nommée classe parent et celle créée en se basant sur celle-ci classe enfant ou fille.

Grâce à l’héritage, la classe fille aura déjà tous les attributs et toutes les méthodes définies dans la classe parent. Ces éléments sont obtenus en héritage, un peu comme les enfants héritent génétiquement de leurs parents : un enfant a par exemple la même apparence que son père et les mêmes yeux que sa mère. La grande différence par rapport aux humains, c’est que les classes n’ont qu’un parent.

En plus de ces éléments hérités, il sera possible d’ajouter des attributs et des méthodes supplémentaires.

Un enfant peut avoir des éléments qui lui sont propres, par exemple une cicatrice sur le front.

Pour qu’une classe hérite d’une autre classe, il faut que la classe fille soit un cas particulier de la classe parent. Pour s’assurer de cela, n’hésitez pas à vous poser la question suivante : X est-il un cas particulier de Y ? Si oui, alors la classe X peut hériter de la classe Y. Par exemple : un vélo est-il un cas particulier de véhicule ? Oui, alors la classe Velo peut hériter d’une classe Vehicule. Par contre, à la question : une trottinette est-elle un cas particulier de vélo, nous répondons non. La classe Trottinette n’héritera donc pas de la classe Velo.

Les classes peuvent donc être vues comme une hiérarchie où les catégories les plus générales seraient les classes au sommet de cette hiérarchie, et de ces classes hériteraient des sous-catégories plus spécifiques.

Il faut faire tout de même attention à ne pas abuser de l’héritage. Un trop grand nombre de niveaux d’héritage nuit à la performance du programme et à...

Les classes internes en Java

En Java, il est possible de définir une classe au sein d’une autre classe. Bien souvent, il s’agit d’une classe privée qui est utilisée uniquement au sein de cette classe. La classe interne se nomme nested class en anglais.

1. La définition

Syntaxe :

visibilité class NomDeLaClasseEnglobante {  
  ...  
   
  private [static] class NomDeLaClasseInterne {  
    ...  
  }  
} 

La classe interne est définie au même niveau qu’un attribut ou une méthode de la classe englobante. Si le mot-clé static est positionné, alors il s’agit d’une classe utilitaire interne qui ne pourra contenir que des méthodes de classe. Ce cas de figure est plutôt rare. Une classe interne est généralement créée pour que la classe englobante puisse créer une ou plusieurs instances, mais que ces instances ne soient utilisées qu’au sein de cette classe englobante.

Une classe interne est définie comme habituellement, à la différence près de son emplacement inhabituel au sein d’une classe et de sa visibilité private. Elle peut définir comme toute autre classe, ses attributs d’instance ou de classe, ses constantes, ses constructeurs et ses méthodes d’instance ou de classe.

Par exemple, il est possible de définir une classe Commande définissant en son sein une classe Article. Cette classe Article n’est alors connue et utilisée que par la classe Commande. Il ne doit donc avoir aucun constructeur ou méthode publique qui prend un paramètre de type Article ou qui retourne un type Article. En revanche, tout ceci est possible si le constructeur ou la méthode est privé.

Exemple :

import java.time.LocalDateTime;  ...

La création d’instances par méthode de classe en Java

Les méthodes de classe n’ont pas besoin d’une instance pour être appelées. Elles sont par contre situées au sein de la classe et ont donc accès aux éléments privés (à condition que ce ne soient pas des éléments d’instance). En particulier, ces méthodes ont accès à un constructeur ayant une visibilité privée. Une méthode de classe peut donc créer une instance en y faisant appel et retourner cette instance. Cette technique se nomme une fabrique d’instance par méthode de classe.

1. L’encapsulation des constructeurs

À l’instar des attributs qui sont protégés en application du principe d’encapsulation, l’accès aux constructeurs peut également être contrôlé. Pour mettre cela en place, il faut que la classe ne possède que des constructeurs privés, ou protégés si des classes en héritent. Si la classe ne possède aucun constructeur explicite, n’oubliez pas qu’un constructeur par défaut est fourni et que celui-ci est public. Dans ce cas, vous pouvez créer explicitement un constructeur avec une visibilité privée ou protégée ne prenant pas d’argument et n’ayant aucune instruction, ainsi le constructeur par défaut n’est pas ajouté.

Depuis l’extérieur de la classe, il n’est plus possible de faire appel à un constructeur de celle-ci. Le seul moyen pour créer une instance est donc de faire appel à une méthode de classe retournant une instance de celle-ci. Cette encapsulation permet de n’appeler le constructeur qu’une fois que toutes les règles métier ont été vérifiées. La création d’une instance est un processus coûteux, ainsi, le constructeur n’est appelé que si réellement une instance doit être créée (lorsqu’une règle métier est vérifiée au sein d’un constructeur public et n’est pas satisfaite, il est possible d’interrompre le traitement, mais "le mal est déjà fait !"). De plus, afin de produire un code efficace...

Exercices

1. La bataille de dés

Prérequis : Exercice 1 du chapitre La programmation orientée objet

Deux joueurs s’affrontent dans une bataille de dés : chaque joueur possède un dé à six faces et dix jetons.

Créer une classe JoueurBataille ayant comme attributs d’instance le nombre de jetons, un dé (une instance de la classe De) et le nom du joueur. Ajouter au sein de cette classe les autres éléments nécessaires. Créer un algorithme principal utilisant cette classe.

Exemple d’exécution :

Nom du joueur ?

Jeanne

Nom du joueur ?

Léonie

Jeanne a fait un 4

Léonie a fait un 1

Jeanne remporte

Score : Jeanne 11 - Léonie 9

Jeanne a fait un 1

Léonie a fait un 5

Léonie remporte

Score : Jeanne 10 - Léonie 10

Jeanne a fait un 5

Léonie a fait un 2

Jeanne remporte

Score : Jeanne 11 - Léonie 9

Jeanne a fait un 1

Léonie a fait un 6

Léonie remporte

Score : Jeanne 10 - Léonie 10

Jeanne a fait un 3

Léonie a fait un 6

Léonie remporte

Score : Jeanne 9 - Léonie 11

Jeanne a fait un 5

Léonie a fait un 6

Léonie remporte

Score : Jeanne 8 - Léonie 12

Jeanne a fait un 2

Léonie a fait un 2

égalité

Score : Jeanne 8 - Léonie 12

Jeanne a fait un 6

Léonie a fait un 6

égalité

Score : Jeanne 8 - Léonie 12

Jeanne a fait un 3

Léonie a fait un 6

Léonie remporte

Score : Jeanne 7 - Léonie 13

Jeanne a fait un 1

Léonie a fait un 5

Léonie remporte

Score : Jeanne 6 - Léonie 14

Jeanne a fait un 2

Léonie a fait un 5

Léonie remporte

Score : Jeanne 5 - Léonie 15

Jeanne a fait un 3

Léonie a fait un 5

Léonie remporte

Score : Jeanne 4 - Léonie 16

Jeanne a fait un 2

Léonie a fait un 3

Léonie remporte

Score : Jeanne 3 - Léonie 17

Léonie a fait un 5

Léonie remporte

Score : Jeanne 2 - Léonie 18

Jeanne a fait un 1

Léonie a fait un 6

Léonie remporte

Score : Jeanne 1 - Léonie 19

Jeanne a fait un 1

Léonie a fait un 2

Léonie remporte

Score : Jeanne 0 - Léonie 20

Léonie gagne !

2. Les clients (version 2)

Prérequis : Exercice 2 du chapitre La programmation orientée objet

Les clients créés au chapitre précédent...

Solutions des exercices

1. La bataille de dés

Classe JoueurBataille  
Attribut nbJetons : entier  
Attribut de : De  
Attribut nom : texte  
Méthodes  
  
Constructeur()  
Début  
 instance.nom <- saisir("nom du joueur ?")  
 instance.nbJetons <- 10  
 instance.de <- nouveau De()  
Fin  
  
Fonction tourDeJeu() Retourne entier  
Début  
 écrire(instance.nom & " a fait un " & instance.de.lancer())  
 Retourner instance.de.getFaceTiree()  
Fin  
  
Fonction perdUnJeton() Retourne booléen  
Début  
 instance.nbJetons <- instance.nbJetons - 1  
 Retourner instance.nbJetons = 0  
Fin  
  
Procédure gagneUnJeton()  
Début  
 instance.nbJetons <- instance.nbJetons + 1  
Fin  
  
Fonction getNom() Retourne texte  
Début  
 Retourner instance.nom  
Fin  
  
Fonction getNbJetons() Retourne entier  
Début  
 Retourner instance.nbJetons  
Fin  
  
FClasse 

 

Algo BatailleDeDes  
Variable j1 : JoueurBataille <- nouveau JoueurBataille()  
Variable j2 : JoueurBataille <- nouveau JoueurBataille()  
Variable fin : booléen  
Variable score1, score2 : entier  
Début  
 Répéter  
   score1 <- j1.tourDeJeu()  
   score2 <- j2.tourDeJeu()  
   Si score1 > score2 Alors  
     écrire(j1.getNom() & " remporte")  
     j1.gagneUnJeton()  
     fin <- j2.perdUnJeton()  
   Sinon  
     Si score1 < score2 Alors  
       écrire(j2.getNom() & " remporte")  
       j2.gagneUnJeton()  
       fin <- j1.perdUnJeton()  
     Sinon  
       écrire("égalité")  ...