La programmation orientée objet
Présentation
1. Qu’est-ce que la programmation orientée objet ?
La programmation orientée objet, souvent abrégée en POO, est un autre paradigme de programmation, qui vient s’ajouter aux paradigmes de programmation impérative et procédurale utilisés précédemment.
Pour introduire ce paradigme de programmation, je prends tout d’abord un exemple, avant de vous donner une définition formelle de ce qu’est la programmation orientée objet. Supposons que nous souhaitions coder un jeu de bataille navale (une vraie, avec deux joueurs s’affrontant et cherchant à couler les bateaux de différents types de l’adversaire). Il serait nécessaire, sans la POO, de définir des variables pour connaître, pour chaque bateau, son type, sa longueur et ses coordonnées. Il serait également nécessaire de faire des procédures et des fonctions permettant de choisir sa position, de tester si un tir touche le bateau… Toutes ces variables, ces fonctions et ces procédures sont des éléments qui, d’un point de vue de l’algorithme, sont indépendants les uns des autres et pourtant, d’un point de vue de la logique, elles forment un tout, elles vont bien ensemble. Le principe de la programmation orientée objet consiste donc à regrouper ces éléments au sein d’un...
Les notions de classe et d’instance
En programmation orientée objet, nous définissons donc des classes représentant les objets que nous souhaitons modéliser. Chaque classe définit un concept : il est possible d’en donner une définition. Par exemple, un bateau de bataille navale est défini par son type, sa longueur et ses coordonnées, mais également par le fait de pouvoir être touché par un tir de l’adversaire, voire coulé. Tous les bateaux de ce jeu respectent cette définition de la classe Bateau. Il y a un ensemble de bateaux intervenant dans une partie de bataille navale. Ces différents bateaux correspondent aux instances de la classe Bateau. Une instance d’une classe est donc un élément respectant la définition de cette classe. Dans notre jeu de bataille navale, il sera ainsi nécessaire de créer une classe Bateau et, à partir de cette classe, de créer dix instances pour représenter les bateaux des deux joueurs.
Pour prendre une image pour illustrer la différence entre ces deux concepts : une classe peut être vue comme un moule et les instances comme les pâtés de sable qui ont été construits à partir de celui-ci. Toutes les instances ont bien une définition commune : elles ont la même forme que le moule, mais ceux-ci...
La déclaration d’une classe
En algorithmique, les classes se déclarent au même niveau que les algorithmes, les sous-algorithmes et les constantes globales.
Syntaxe :
Classe NomDeLaClasse
FClasse
Leur déclaration commence par le mot-clé Classe et se termine par le mot-clé FClasse (Fin de la classe). Le nom d’une classe commence obligatoirement par une majuscule.
En Java, il faut savoir que tout est objet. Donc, sans le savoir, nous déclarions déjà des classes. Les fameuses lignes que nous écrivions systématiquement définissaient la classe dans laquelle nous écrivions nos procédures, nos fonctions…
Syntaxe :
public class NomDeLaClasse {
}
Une classe Java doit être définie dans un fichier portant le même nom avec l’extension .java.
Il est conseillé de définir une classe au sein d’un package. Un package n’est rien d’autre qu’un groupement de classes. Ainsi, toutes les classes portant sur un même sujet sont regroupées au sein d’un même package.
Le nom du package doit être différent des noms des autres packages pour éviter un conflit. Pour ce faire, il est conseillé d’utiliser le nom de domaine de sa société et de le transformer de la manière suivante :
extension.nomDeDomaine.nomDuPackage...
Les attributs d’instance
Les attributs d’instance sont les valeurs caractérisant une instance. La couleur du pion et le fait qu’il a été transformé en dame par exemple. Ces attributs correspondent aux fameuses variables qui vont bien ensemble.
Au sein de la classe, ces attributs sont définis de manière similaire à la définition d’une variable. La seule différence est que le mot-clé Variable est remplacé par le mot-clé Attribut.
Syntaxe :
Attribut nomDeLAttribut : type
Exemple :
Classe Bateau
Attribut type : texte
Attribut longueur : entier
Attribut latitude : entier
Attribut longitude : entier
Attribut horizontal : booléen
Attribut partiesTouchees : entier <- 0
FClasse
Notre classe Bateau définit donc six attributs d’instance : type de type texte (porte-avions, croiseur, contre-torpilleur, sous-marin ou torpilleur), longueur de type entier (longueur en nombre de cases utilisées par le bateau), latitude et longitude de type entier (les coordonnées de la première case utilisée par le bateau), horizontal de type booléen (vrai si le bateau est positionné horizontalement et faux s’il est vertical) et partiesTouchees de type entier (nombre de tirs ayant touché ce bateau). Vous remarquerez que, de manière similaire aux variables...
Les constantes
Il est également possible de déclarer une constante au sein d’une classe en la déclarant dans la section de déclaration avec les attributs, avec la même syntaxe que précédemment.
Exemple :
Classe Bateau
Attribut type : texte
Attribut longueur : entier
Attribut latitude : entier
Attribut longitude : entier
Attribut horizontal : booléen
Attribut partiesTouchees : entier <- 0
Constante TOUCHE : entier <- 1
Constante COULE : entier <- 2
FClasse
Pour pouvoir accéder à la valeur de la constante, il est nécessaire de faire précéder son nom du nom de la classe dans laquelle elle a été définie.
Syntaxe :
NomDeLaClasse.NOM_DE_LA_CONSTANTE
Exemple :
Bateau.COULE
En Java, nous avons déjà défini des constantes au sein d’une classe, il s’agit de nos constantes globales.
Syntaxe :
visibilité static final type NOM_DE_LA_CONSTANTE_GLOBALE = valeur;
Les visibilités disponibles pour les constantes sont les mêmes que celles présentées au paragraphe précédent pour les attributs. Comme la valeur n’est pas modifiable, toutes les visibilités sont envisageables sans enfreindre le principe d’encapsulation.
Pour faire appel à une constante, il faut la faire...
Les méthodes d’instance
Les méthodes d’instance sont les actions réalisables sur les instances. Elles correspondent aux procédures et fonctions qui manipulent les variables allant bien ensemble citées en introduction de ce chapitre.
Une méthode est donc une fonction ou une procédure qui est incluse dans une classe.
1. La déclaration d’une méthode d’instance
Ces méthodes sont déclarées après le mot-clé Méthodes :
Classe NomDeLaClasse
# déclaration des attributs
Méthodes
# déclaration des méthodes
FClasse
Les méthodes d’instance peuvent accéder aux attributs d’instance en préfixant leur nom par instance. Ce mot-clé instance permet d’accéder à l’instance courante, c’est-à-dire l’instance sur laquelle cette méthode est appelée.
Voici cela illustré avec notre classe Bateau :
Classe Bateau
Attribut type : texte
Attribut longueur : entier
Attribut latitude : entier
Attribut longitude : entier
Attribut horizontal : booléen
Attribut partiesTouchees : entier <- 0
Constante TOUCHE : entier <- 1
Constante COULE : entier <- 2
Méthodes
Procédure initialiser(type : texte, longueur : entier)
Début
instance.type <- type
instance.longueur <- longueur
Fin
...
La création d’une instance
1. La déclaration et l’instanciation d’une variable de type classe
Nous savons maintenant créer des classes, il est grand temps d’apprendre à y faire appel ! Ce paragraphe a donc pour objectif d’expliquer comment créer une instance et comment faire appel à ses méthodes.
L’écriture d’une classe correspond à la création d’un nouveau type. Il est donc possible de déclarer des variables de ce type.
Syntaxe :
Variable nomInstance : NomDeLaClasse
Exemple :
Variable croiseur : Bateau
Tout comme pour les tableaux dont la taille n’a pas été indiquée entre les crochets, cette déclaration n’a créé qu’une variable capable de contenir une instance de Bateau. Pour stocker cette instance de Bateau dans cette variable, il faut la créer. Cette opération s’appelle l’instanciation.
Syntaxe :
nom_instance <- nouveau NomDeLaClasse()
Exemple :
croiseur <- nouveau Bateau()
Il est également possible de créer simultanément l’instance et la variable permettant de la stocker en écrivant :
Variable nomDeLInstance : NomDeLaClasse <- nouveau NomDeLaClasse()
Une fois l’instance créée, il est possible de faire appel à ses méthodes d’instance en utilisant la syntaxe suivante :
nomDeLInstance.nomDeLaMethodeDInstance(paramètres)
Voici un exemple d’algorithme utilisant la classe Bateau :
Algo TestBateau
Variable croiseur : Bateau <- nouveau Bateau()
Variable resultatTir : entier
Début
croiseur.initialiser("Croiseur", 4)
# positionnement dans le sens vertical avec pour première case
#(3; 7)
croiseur.positionner(3, 7, FAUX)
# tir en (2;4) : normalement, le bateau n'est pas touché
resultatTir <- croiseur.testerTir(2, 4)
Si resultatTir = 0 Alors
écrire("Un tir hors du bateau ne lui a pas causé de dégâts ! " &
Tout va bien !")
Sinon
écrire("Il y a un problème ! Un tir hors du bateau...
Les constructeurs
Lors de la création d’une instance (exemple : croiseur <- nouveau Bateau()), différentes opérations sont réalisées. Tout d’abord, il est nécessaire de réserver l’espace mémoire nécessaire pour stocker les informations de l’instance (cet aspect-là est expliqué dans le chapitre La mémoire). Ensuite, les attributs d’instances sont initialisés grâce au constructeur. Enfin, la variable devant contenir l’instance est valorisée.
1. Le constructeur par défaut
En algorithmique comme en Java, dans une classe, en l’absence de la déclaration d’au moins un constructeur, il existe un constructeur par défaut. Ce constructeur est alors implicitement ajouté à la classe. Le constructeur par défaut ne prend pas d’argument. Il fait appel au constructeur sans argument de la classe parent (cela est abordé dans la section L’héritage du chapitre prochain) et initialise les attributs à leurs valeurs par défaut (faux pour des booléens, zéro pour des nombres, le caractère nul pour les caractères et l’absence de valeur (null) pour les instances et les tableaux).
Dans l’exemple de la bataille navale, aucun constructeur n’a été explicitement déclaré. Un constructeur par défaut y a donc implicitement été ajouté et c’est celui-ci qui était appelé lors de la création de l’instance.
2. Les constructeurs
Il est possible de déclarer un ou des constructeurs afin d’initialiser les attributs d’instance avec des valeurs plus adaptées ou des valeurs choisies. Pour définir un constructeur, il faut utiliser la syntaxe suivante au sein de la section Méthodes de la classe :
Constructeur(listeDesParamètres)
Début
#Instructions
Fin
Par exemple, il est possible de définir un constructeur pour la classe Bateau :
Classe Bateau
Attribut type : texte
Attribut longueur : entier
Attribut latitude : entier
Attribut longitude : entier
Attribut horizontal : booléen
Attribut partiesTouchees : entier <- 0
...
Les attributs de classe
Il existe deux catégories d’attributs :
-
Les attributs d’instance
-
Les attributs de classe
La première catégorie, les attributs d’instance présentés à la section du même nom dans ce chapitre, représente des éléments qui sont propres à chaque instance. Dans l’exemple de la bataille navale, chaque instance de bateau possède son propre type, sa propre longueur, son propre positionnement (latitude, longitude et orientation verticale ou horizontale) et son propre compteur de tirs l’ayant endommagé. Pour tous ces attributs d’instance, une modification de valeur n’affecte qu’une seule instance. Par exemple, si un tir atteint l’un des bateaux d’un joueur, seule la valeur de l’attribut partiesTouchees de ce bateau est modifiée. Les valeurs de cet attribut pour toutes les autres instances restent inchangées.
La seconde catégorie, les attributs de classe, représente les éléments qui sont communs ou collectifs à la classe.
Les éléments communs correspondent à une valeur qui serait la même pour l’ensemble des instances de cette classe. Le caractère pour afficher une partie de bateau indemne et celui pour afficher une partie de bateau atteinte par un tir sont des éléments communs à toutes les instances....
Les méthodes de classe
Les méthodes de classe, à l’instar des attributs de classe, sont des méthodes dont l’exécution ne dépend pas d’une instance particulière. Ces méthodes travaillent donc sur la classe et non sur les instances de celles-ci.
Par exemple, une méthode de classe pourrait afficher les règles de positionnement d’un bateau sur la grille de la bataille navale. Cette méthode ne dépend pas d’un bateau plutôt que d’un autre, c’est donc une méthode de classe. Les méthodes de classe peuvent accéder aux attributs de classe, par contre elles ne peuvent pas accéder aux attributs d’instance puisque ces attributs dépendent d’une instance.
1. La déclaration d’une méthode de classe
Les méthodes de classe sont définies au sein d’une section MethodesDeClasse.
Syntaxe :
Classe NomDeLaClasse
# déclaration des attributs
Méthodes
# déclaration des méthodes d'instance
MéthodesDeClasse
# déclaration des méthodes de classe
FClasse
Exemple :
Classe Bateau
Attribut type : texte
Attribut longueur : entier
Attribut latitude : entier
Attribut longitude : entier
Attribut horizontal : booléen
Attribut partiesTouchees : entier <- 0
AttributDeClasse symboleTouche : caractère <- 'x'
Constante TOUCHE : entier <- 1
Constante COULE : entier <- 2
Méthodes
Constructeur(type : texte, longueur : entier)
Début
instance.type <- type
instance.longueur <- longueur
Fin
Constructeur(type : texte, longueur : entier, latitude : entier,
longitude : entier, horizontal : booléen) AutreConstructeur(type,
longueur)
Début
positionner(latitude, longitude, horizontal)
Fin
Fonction getType() Retourne texte
Début
Retourner...
Les instances : un type référence
Les instances contiennent un ensemble de valeurs, tout comme les tableaux. Pour les mêmes raisons d’optimisation de l’utilisation des zones mémoire, les instances sont des types référence. L’utilisation de la mémoire et la manière d’y stocker une instance est présentée plus en détail dans le chapitre La mémoire. Pour l’instant, il faut être conscient que lorsqu’une instance est passée en paramètre, est retournée par une méthode ou une fonction ou bien est affectée à une autre variable, aucune copie d’instance n’a été réalisée.
Ce qui est recopié, c’est la référence vers cette instance.
Par exemple, supposons qu’une instance de bateau est passée en paramètre. Si une modification est effectuée au sein de la méthode, la modification est effective dans l’instance même après la fin de l’appel de la méthode. Car dans cet exemple, il n’y a qu’une seule instance de bateau et plusieurs références permettant d’accéder à celle-ci.
Exercices
1. Les dés
Créer une classe modélisant un dé à jouer. Cette classe possède :
-
deux attributs d’instance : l’un permettant de connaître le nombre de faces que possède le dé et l’autre pour stocker la dernière face qui a été tirée ;
-
deux constructeurs : l’un prenant en paramètre le nombre de faces pour le dé à créer et l’autre, sans paramètre, créant un dé classique à six faces ;
-
des méthodes getter pour les deux attributs ;
-
une méthode setter uniquement pour changer le nombre de faces ;
-
une méthode d’instance permettant de simuler le lancer de dé, retournant la valeur tirée aléatoirement par le dé.
Écrire un algorithme principal utilisant cette classe pour créer trois dés : l’un à six faces, un autre à dix faces et le dernier à douze faces. Les trois dés sont lancés jusqu’à ce que la somme des dés soit supérieure ou égale à vingt.
2. Les clients
Pour les besoins d’un site de vente en ligne de livres d’informatique, les éditions ENI par exemple, il vous est demandé de créer une classe permettant de modéliser un client. Les clients ont tous un nom et vous devez leur attribuer un numéro de client. Écrivez également un algorithme de test permettant de saisir et d’afficher les clients.
Exemple d’exécution possible :
-- Menu -- |
1 - Afficher les clients |
2 - Créer un client |
3 - Quitter |
2 |
Nom ? |
Francine SCHNEIDER |
Client n°1 [Francine SCHNEIDER] ajouté... |
Solutions des exercices
1. Les dés
Classe De
Attribut nbFaces : entier
Attribut faceTiree : entier
Méthodes
Constructeur(nbFaces : entier)
Début
instance.nbFaces <- nbFaces
instance.faceTiree <- lancer()
Fin
Constructeur() AutreConstructeur(6)
Début
Fin
Fonction getNbFaces() Retourne entier
Début
Retourner instance.nbFaces
Fin
Fonction getFaceTiree() Retourne entier
Début
Retourner instance.faceTiree
Fin
Procédure setNbFaces(nbFaces : entier)
Début
instance.nbFaces <- nbFaces
Fin
Fonction lancer() Retourne entier
Début
instance.faceTiree <- aléa(1, instance.nbFaces)
Retourner instance.faceTiree
Fin
FClasse
Algo TestDes
Constante SEUIL : entier <- 20
Variable de6, de10, de12 : De
Début
de6 <- nouveau De()
de10 <- nouveau De(10)
de12 <- nouveau De(12)
Répéter
écrire("tentative d'arriver à " & SEUIL)
écrire("Lancement du dé à six faces : " & de6.lancer())
écrire("Lancement du dé à dix faces : " & de10.lancer())
écrire("Lancement du dé à douze faces : " & de12.lancer())
TantQue de6.getFaceTiree() + de10.getFaceTiree() +
de12.getFaceTiree() < SEUIL FRépéter
écrire("Victoire !")
Fin
2. Les clients
Classe Client
Attribut nom : texte
Attribut numCli : entier
AttributDeClasse nbClients : entier <- 0
Méthodes
Constructeur(nom : texte)
Début
instance.nom <- nom
Client.nbClients <- Client.nbClients...