Cours de Programmation Objet BTS SIO SLAM
Cours de Programmation Objet BTS SIO SLAM
PROGRAMMATION OBJET
COURS
PROGRAMMATION
OBJET
COURS
Retrouvez la liste de nos formations sur www.cned.fr
Pour plus d’informations, appelez le 05 49 49 94 94
Du lundi au vendredi, 8 h 30-18 h.
Coût d’une communication ordinaire.
*82950TGPA0013*
Sommaire
Conseils généraux 3
Séquence 1 : Notions fondamentales 5
Séquence 2 : Héritage et polymorphisme 27
Séquence 3 : Classification, regroupement et opérations associées 45
Séquence 4 : Persistance des objets 75
Séquence 5 : Notions évoluées 87
Séquence 6 : UML, la modélisation objet 99
Séquence 7 : Autocorrection 127
Les cours du CNED sont strictement réservés à l’usage privé de leurs destinataires et ne sont pas destinés à une utilisation collective.
Les personnes qui s’en serviraient pour d’autres usages, qui en feraient une reproduction intégrale ou partielle, une traduction sans
le consentement du CNED, s’exposeraient à des poursuites judiciaires et aux sanctions pénales prévues par le Code de la propriété
intellectuelle. Les reproductions par reprographie de livres et de périodiques protégés contenues dans cet ouvrage sont effectuées
par le CNED avec l’autorisation du Centre français d’exploitation du droit de copie (20, rue des Grands Augustins, 75006 Paris).
© CNED 2013
Conseils généraux
Le fascicule de cours contient toutes les notions fondamentales exigées pour l’examen.
Vous devez donc l’étudier en détail et contrôler, par les exercices, que les connaissances
sont bien acquises.
Le fascicule de travaux pratiques comporte un unique TP qu’il faudra réaliser après
l’étude complète du cours. Ce TP est une mise en pratique directe de toutes les notions
abordées. Sa réalisation est indispensable pour contrôler votre niveau.
Vous avez 2 devoirs à renvoyer :
Le premier devoir est à réaliser à l’issue de la séquence 3 : il porte sur toutes les notions
de programmation objet. Le second devoir est à réaliser à l’issue du cours : il inclut aussi
le diagramme de classes.
Organisation du fascicule
Ce fascicule s’articule en 7 séquences :
Séquence 1 : Notions fondamentales
La plupart de ces notions ont déjà été abordées dans le module « Bases de la pro-
grammation », mais cette fois elles sont vues en détail et avec la vision du déve-
loppeur et non du simple utilisateur d’objets.
Séquence 2 : Héritage et polymorphisme
Là encore, les notions déjà vues sous l’angle de l’utilisateur sont approfondies et
de nouveaux aspects sont présentés.
8 2950 TG PA 00
Séquence 3 : Classification, regroupement et opérations associées
Vous allez approfondir les notions de collections, le regroupement de classes et
découvrir des opérations complémentaires.
Séquence 4 : Persistance des objets
Une petite séquence juste pour aborder un point qui reste fondamental : comment
enregistrer le contenu des objets pour une réutilisation ultérieure.
Séquence 5 : Notions évoluées
Cette séquence va vous faire découvrir les design patterns, les tests unitaires et les
outils d’aide au développement proposés par les environnements de développe-
ment.
Séquence 6 : UML, la modélisation objet
Cette dernière séquence de cours permet de présenter UML qui est à l’heure
actuelle incontournable dans la modélisation objet.
Séquence 7 : Autocorrection
Vous trouverez ici la correction des exercices de ce fascicule.
Excepté l’autocorrection qui est à consulter au fur et à mesure des besoins, les
séquences du cours sont à traiter dans l’ordre et dans leur intégralité. Ne faites
pas d’impasse sur le cours ou les exercices : vous risqueriez d’avoir des lacunes
pénalisantes pour la suite.
Bon courage !
Conseils généraux
Page 4
8 2950 TG PA 00
Séquence 1
Notions fondamentales
Cette première séquence contient en grande partie un rappel des notions
abordées dans le module 2944 « Bases de la programmation », séquence
« Initiation à la programmation objet ». Cependant, ces notions fonda-
mentales sont un peu plus approfondies et complétées par de nouvelles
notions. Il est donc important d’étudier cette séquence même si vous avez
le sentiment d’avoir bien assimilé les notions déjà abordées.
X Prérequis
Avoir de bonnes bases en programmation procédurale et de préférence avoir
déjà eu l’occasion de manipuler des objets.
X Contenu Notions
fondamentales
1. Qu’est-ce qu’un objet ? .................................................................................... 6
2. Exemples de classes.......................................................................................... 7 Page 5
Synthèse ........................................................................... 26
8 2950 TG PA 00
1. Qu’est-ce qu’un objet ?
Vous avez déjà eu l’occasion de manipuler des objets en programmation :
• des objets graphiques en VB6 dans le module 2944 « Bases de la programmation »
(boutons, listes…) ;
• des objets graphiques et non graphiques en C# dans le même module (objets créés
à partir de la classe Reveil) ;
• des objets aussi en javascript dans le module 2946 « Développement d’applications » .
Jusqu’à maintenant, vous avez donc manipulé des objets, vous les avez même parfois
créés, mais vous avez toujours travaillé avec des classes existantes où dont le contenu
vous était donné. Cela vous a tout de même donné une bonne idée de ce qu’est un objet.
Rappelons tout de même qu’un objet est une variable complexe qui peut comporter :
• des propriétés pour le décrire (nom, couleur, taille...) ;
• des méthodes pour agir dessus (par exemple l’ajout dans une liste).
À la fin de ce cours, vous allez créer un petit jeu 2D dont une partie de l’interface va
ressembler à ceci :
Séquence 1
Notions
fondamentales
Page 6
8 2950 TG PA 00
Plusieurs personnages vont se retrouver dans une arène, et vont se battre. Avec vos
connaissances actuelles, pour réaliser ce type d’application, vous devriez prévoir par
exemple un tableau de structure pour mémoriser les noms et scores des différents
joueurs, ainsi que le personnage associé (pour simplifier dans le cours, on parlera de
couleur). Vous auriez aussi à écrire les différents modules qui permettent d’afficher la
fenêtre de faire déplacer les joueurs, de les faire se battre, de gérer les points de chaque
joueur, mais aussi des modules pour gérer le petit "chat" (car il y aura aussi une zone de
discussion), etc…
La programmation objet vous propose d’utiliser mais aussi de créer des types complexes
pour gérer les "objets" de votre programme. Par exemple, on peut imaginer créer un
type "Joueur" qui non seulement contiendra les caractéristiques du joueur (nom, score,
couleur…) mais aussi les modules spécifiques au joueur (pour se déplacer, pour se battre,
pour mourir…).
Rappelez-vous qu’un objet est une variable complexe dont le type est UNE CLASSE.
2. Exemples de classes
Voici un premier exemple de classe, correspondant à des objets graphiques que vous
avez déjà utilisés en événementiel :
Classe Liste
propriétés :
element[1..n] : chaîne Séquence 1
8 2950 TG PA 00
Voici un troisième exemple de classe que vous allez apprendre à créer pour le jeu de
combat, et qui va permettre de gérer les joueurs :
Classe Joueur
propriétés :
nom : chaîne
couleur : chaîne Vous allez regrouper tout ce
vie : entier qui concerne le joueur dans
... une même classe. Il sera alors
méthodes : possible de créer plusieurs
objets issus de cette classe.
avancer(sens : entier)
frapper()
mourir()
...
Les propriétés, méthodes et événements sont appelés LES MEMBRES d’une classe.
8 2950 TG PA 00
Déclaration
La déclaration d’un objet se fait comme pour une variable simple : il suffit de préciser
son type, donc la classe concernée.
Une classe est un type complexe. Vous découvrirez dans la suite de ce cours, qu’il existe
plusieurs catégories de classes (classes métiers, classes techniques, classes génériques…).
Mais quelle que soit la catégorie, toutes les classes possèdent cette structure regroupant
un ensemble de membres liés par un sens commun.
Création
Cette notion est supplémentaire par rapport aux variables simples : après l’avoir déclaré
et avant de l’utiliser, un objet doit être créé. Le type de l’objet étant complexe, il est
nécessaire de passer par une étape de création qui s’occupe entre autre de réserver la
place de l’objet en mémoire. La création utilise le mot réservé "new" que l’on retrouve
dans tous les langages. Cependant, suivant les langages, cette étape est parfois option-
nelle : prenez tout de même l’habitude de créer les objets avant de les exploiter (sauf si
vous transférez un objet dans un autre).
Créer un objet revient à INSTANCIER UNE CLASSE.
Une classe est unique. En revanche, il peut exister plusieurs instances (objets) d’une
même classe.
Utilisation
Une fois l’objet déclaré et créé, il est possible de l’utiliser. Pour accéder à l’une de ses
propriétés, il suffit de faire suivre le nom de l’objet par un point puis du nom de la pro- Séquence 1
priété. Même technique pour accéder à une méthode. Cette syntaxe n’est pas nouvelle : Notions
vous avez déjà eu l’occasion d’utiliser des objets et d’accéder à leurs membres. fondamentales
8 2950 TG PA 00
Exercice 1
Écrire la procédure test qui permet de déclarer un objet unjoueur de type Joueur
(et non un tableau) et de lui affecter le nom "test", la couleur "jaune" et 50 de vie.
Puis, afficher la couleur de ce joueur et le faire avancer dans le sens 1 (qui sera par
exemple le sens gauche).
4. Encapsulation
À partir de là, on va analyser différents problèmes qui peuvent se poser, et comment la
programmation objet a résolu chaque problème.
Reprenons la classe Joueur :
Classe Joueur
nom : chaîne
couleur : chaîne
vie : entier
...
avancer(sens : entier)
frapper()
mourir()
...
Séquence 1
Notions
Problème
fondamentales Il ne faut surtout pas que le programme puisse modifier directement la vie ou faire
mourir un joueur. La vie ne doit être modifiée que par la méthode frapper() et le joueur
Page 10 meurt que s’il n’a plus de vie. De l’extérieur de la classe, si la vie est modifiée, elle risque
d’être mise à 0 sans que la méthode mourir() soit appelée. Du coup, on va se retrouver
avec un joueur vivant (méthode mourir non appelée) mais sans vie (vie=0) !
Solution
Protéger certains membres pour qu’ils ne soient pas accessibles de l’extérieur.
On obtient la classe suivante :
Classe Joueur
privé :
vie : entier
mourir()
...
public :
nom : chaîne
couleur : chaîne
avancer(sens : entier)
frapper()
...
En reprenant l’exemple du tableau de joueurs, il ne sera donc pas permis d’écrire :
tJoueur[k].vie Å 5
...
tJoueur[k].mourir()
8 2950 TG PA 00
Les membres privés ne sont accessibles que dans la classe. Les membres publics sont
accessibles de l’extérieur.
Encapsulation = protection de certains membres.
classe Joueur
privé : La partie privée n’est
accessible que par les
vie : entier
méthodes de la classe
mourir()
…
public :
nom : chaine
couleur : chaine INTERFACE
(partie accessible de l’extérieur)
avancer(sens : entier)
frapper()
…
Séquence 1
À partir d’un programme utilisant la classe Joueur, voyons ce qui est permis :
var Notions
fondamentales
k : num
tJoueur[1..9] : Joueur
Page 11
debut
...
tJoueur[k] Å new Joueur()
... INTERDIT
tJoueur[k].vie Å 25 car vie est en privé
...
PERMIS
tJoueur[k].frapper() car frapper() est en public
...
Fin
Il est très important de bien réfléchir sur l’encapsulation des membres et de faire les bons choix pour
la protection et la stabilité des objets. Le but est de protéger le maximum de membres. C’est d’autant
plus important quand les programmeurs qui utilisent les classes ne sont pas ceux qui ont écrit ces
mêmes classes (ce qui est assez courant).
8 2950 TG PA 00
Exercice 2
Dans les deux programmes suivants, dites ce qui est interdit et ce qui est permis.
Premier programme : imaginons la méthode estfrappé() dans la classe Joueur.
Séquence 1
Notions
6. Abstraction
fondamentales
La notion d’encapsulation qui vient d’être présentée, permet de gérer des niveaux d’abs-
Page 12 traction (de protection) dans les classes. Rien de nouveau, juste un peu de vocabulaire
sur ces notions de protections de données.
Envoi de message
L’envoi de message à un objet représente l’appel d’une méthode publique de la classe
correspondante. C’est le seul moyen pour interagir sur l’objet. L’objet est dit "receveur".
Niveaux d’abstraction
L’extérieur accède à l’objet par envoi de messages (utilisation de l’interface, donc de la
partie publique, de la classe), et l’interface s’occupe d’agir sur la partie privée.
8 2950 TG PA 00
Tant que vous êtes utilisateur de la classe, vous n’avez accès qu’à la partie interface. Le
reste ne vous concerne pas. Si vous êtes le concepteur de la classe, vous avez une vision
totale et c’est à vous de décider ce qui sera ou non accessible de l’extérieur.
7. Accesseur / Mutateur
Traditionnellement, les propriétés d’une classe sont mises en privées. Sans être une obli-
gation, ce principe (propriétés en privé) est fortement conseillé et respecté. Donc, si les
propriétés sont en privées, l’interface ne contient alors que des méthodes et ce sont les
méthodes qui vont permettre d’accéder aux propriétés.
Problème
Comment accéder aux propriétés privées, pour juste modifier ou récupérer sa valeur ?
Solution
Créer des méthodes spécifiques : certaines vont servir à récupérer la valeur des propriétés
(ACCESSEUR appelé aussi GETTER), d’autres vont servir à la modifier (MUTATEUR appelé
aussi SETTER).
Voici la nouvelle classe Joueur :
Classe Joueur
privé :
vie : entier Séquence 1
nom : chaîne
Notions
couleur : chaîne fondamentales
mourir()
... Page 13
public :
avancer(sens : entier)
frapper()
estfrappé()
...
Toutes les propriétés ont été mises en privé, et bien sûr la méthode mourir() a été main-
tenue en privé.
Imaginons qu’il doit être possible de modifier le nom d’un joueur, à partir d’un objet de
type Joueur. Il faut une méthode (un SETTER) qui va recevoir le nouveau nom et modifier
la propriété privée en conséquence.
Joueur::setNom(unnom : chaîne)
debut La méthode reçoit unnom en paramètre,
nom Å unnom et la propriété privée nom est valorisée
fin (remplie) avec le contenu du paramètre.
Un setter est traditionnellement appelé "set" suivi du nom de la propriété, avec une
majuscule. Cette notation est fortement conseillée (mais ce n’est pas une obligation).
Imaginons qu’il soit nécessaire d’afficher la couleur du joueur, à partir d’un objet de type
Joueur. Il faut une méthode (un GETTER) qui va retourner le contenu de la propriété
privée correspondante.
8 2950 TG PA 00
Joueur::getCouleur() : chaîne
debut
La méthode retourne le contenu
retourner couleur
de la propriété privée couleur.
fin
Comme pour le setter, le getter est traditionnellement appelé "get" suivi du nom de la
propriété, avec une majuscule.
O Seuls les setters et getters nécessaires doivent être créés. Chaque propriété
d’une classe ne possède donc pas forcément un setter et un getter.
Au final, en prenant en compte les 2 méthodes qui viennent d’être écrites, on obtient la
classe Joueur suivante :
Classe Joueur
privé :
vie : entier
nom : chaîne
couleur : chaîne
mourir()
...
public :
setNom(unnom : chaîne)
getCouleur() : chaîne
avancer(sens : entier)
frapper()
Séquence 1 estfrappé()
...
Notions
fondamentales
Comme cela a été déjà dit dans le module 2944 « Bases de la programmation », il est rappelé que
Page 14 beaucoup de développeurs débutants (et mêmes d’autres) pensent qu’il faut obligatoirement mettre
toutes les propriétés en privé et toutes les méthodes en public. Même s’il est effectivement très conseil-
lé de mettre toutes les propriétés en privé, il est très courant (et fortement conseillé) de mettre aussi
des méthodes en privé : celles qui ne doivent être accessibles que par les autres méthodes de la classe.
Exercice 3
8 2950 TG PA 00
8. Constructeur
Problème
Il est parfois nécessaire de réaliser des traitements au moment où l’objet est créé, avant
même de commencer à l’utiliser. Par exemple, pour un joueur, il faut que sa vie soit ini-
tialisée dès le départ.
Solution
Avoir une méthode qui s’exécute automatiquement au moment de la création de l’objet.
Cette méthode s’appelle le CONSTRUCTEUR.
Certains langages utilisent le nom "init" ou d’autres variantes pour désigner le constructeur. Mais la
majorité des langages, C++ et Java en tête, utilisent le nom de la classe pour nommer le constructeur.
En écriture algorithmique, nous utiliserons le nom de la classe.
Voici la nouvelle classe Joueur, avec son constructeur. On va partir du principe que dans
le cas de cette classe, le constructeur doit initialiser les 3 propriétés privées (le nom, la
Séquence 1
couleur et la vie).
Classe Joueur Notions
fondamentales
privé :
vie : entier
Page 15
nom : chaîne
couleur : chaîne
mourir()
...
public :
Joueur(lenom : chaîne, lacouleur : chaîne)
setNom(unnom : chaîne)
getCouleur() : chaîne
avancer(sens : entier)
frapper()
estfrappé()
...
Voyons maintenant le contenu du constructeur. Seuls 2 paramètres ont été précisés. Cela
suppose que la vie va être initialisée autrement. Effectivement, la vie de départ doit être
la même pour tous les joueurs. On va l’initialiser à 100, par exemple.
Joueur::Joueur(lenom : chaîne, lacouleur : chaîne)
debut
Les 2 paramètres servent à
nom Å lenom valoriser les propriétés privées.
couleur Å lacouleur
vie Å 100
fin
8 2950 TG PA 00
Utilisation d’un constructeur
Lors de la création de l’objet, il ne faut pas oublier qu’après le mot "new", c’est en fait
le constructeur qui est appelé. Donc, si le constructeur possède des paramètres, il faut
penser à lui envoyer les valeurs correspondantes.
var
k : num
tJoueur[1..9] : Joueur
debut
...
tJoueur[k] Å new Joueur("Ed", "noir")
...
Fin
Parfois, le constructeur ne comporte aucun paramètre, voire même n’existe pas. Dans ces
2 cas, il faudra tout de même mettre les parenthèses :
...
tJoueur[k] Å new Joueur()
...
Exercice 4
Écrire la procédure initJoueurs, externe à la classe Joueur, qui possède en paramètre
de sortie le tableau tJoueur, de 10 cases, devant contenir des objets de type Joueur.
Cette procédure doit créer les 10 joueurs en demandant à l’utilisateur le nom et la
Séquence 1
couleur de chaque joueur. Le constructeur utilisé est celui qui a été donné à la page
Notions précédente.
fondamentales
Page 16
9. Destructeur
Après avoir vu le constructeur, il est assez facile d’imaginer ce qu’est un destructeur.
Problème
Il est parfois nécessaire d’exécuter des traitements au moment où l’objet est détruit (en
fin de vie de l’objet). Par exemple, lorsqu’un joueur meurt, l’objet correspondant est
détruit et à ce moment là, un message doit préciser que ce joueur est mort.
Solution
Avoir une méthode qui s’exécute automatiquement au moment de la destruction de
l’objet. Cette méthode s’appelle le DESTRUCTEUR ou finaliseur.
8 2950 TG PA 00
Quand l’objet est-il détruit ?
En fin de vie du module ou du bloc dans lequel il a été déclaré, ou lorsqu’il est libéré
dynamiquement (dans le cas d’objets dynamiques non abordés dans ce cours).
Au niveau de la classe Joueur, voilà l’ajout d’un destructeur :
Classe Joueur
privé :
vie : entier
nom : chaîne
couleur : chaîne
mourir()
...
public :
Joueur(lenom : chaîne, lacouleur : chaîne)
~Joueur()
setNom(unnom : chaîne)
getCouleur() : chaîne
avancer(sens : entier)
frapper()
estfrappé()
...
Imaginons que l’objet soit détruit par le programme lorsque le joueur meurt (ce qui
serait possible dans le cas d’un objet dynamique). Le but est alors d’afficher un message
Séquence 1
précisant le nom du joueur qui vient de mourir. Voici le code du destructeur :
Joueur::~Joueur() Notions
debut fondamentales
Problème n°1
Certaines informations doivent être identiques pour toutes les instances d’une classe.
Par exemple, chaque joueur doit avoir son nom propre, ainsi que sa couleur, et sa vie
varie pendant le jeu. Ces informations sont déjà présentes dans la classe et sont propres
à chaque joueur. En revanche, la valeur de la vie au départ est la même pour tous les
joueurs. Elle avait d’ailleurs été initialisée à 100 dans le constructeur. Mais imaginons que
cette valeur mise en dur ne soit pas satisfaisante : le jeu peut très bien offrir des niveaux
de difficulté différents, et donc des valeurs de vie de départ différentes.
Solution n°1
Pouvoir créer une propriété qui aurait la même valeur pour tous les objets d’une classe :
c’est une propriété statique, appelée aussi propriété à portée de classe.
Propriété statique : même valeur pour tous les objets de la classe.
8 2950 TG PA 00
Utilisation d’une propriété statique
Si la propriété est déclarée en publique, elle est accessible directement par le nom de la
classe puisqu’elle n’est pas rattachée à un objet en particulier. Si elle est en privé, elle
sera, comme les autres propriétés, manipulée par les méthodes de la classe. Mais toute
modification de sa valeur sera accessible par tous les objets de la classe.
Voici la classe Joueur, avec en propriété privée departvie qui contiendra la valeur de la
vie de départ pour tous les joueurs :
Classe Joueur
privé :
vie : entier
nom : chaîne
couleur : chaîne
Il faut mettre le mot
static departvie : entier
"static" devant la propriété
mourir()
...
public :
Joueur(lenom : chaîne, lacouleur : chaîne)
...
Problème n° 2
Si la propriété statique est en privé, il faut passer par des méthodes pour y accéder. C’est
dommage de devoir créer un objet pour utiliser une méthode et ainsi accéder à une pro-
Séquence 1
priété qui ne nécessite pas d’objet puisqu’elle concerne directement la classe.
Notions Solution n° 2
fondamentales
Avoir des méthodes qui ne nécessitent pas de passer par un objet pour les utiliser : ce
sont des méthodes statiques, appelées aussi méthodes à portée de classe.
Page 18
Méthode statique : directement accessible par la classe.
O
Accéder à une méthode par le nom de la classe ne se fait que pour les
méthodes statiques.
Cette erreur est suffisamment classique pour insister sur ce point. Très sou-
vent, pour accéder à une méthode, lorsque vous ne trouvez pas d’objet cor-
respondant, vous mettez le nom de la classe. C’est bien sûr totalement faux.
Une méthode s’applique à un objet SAUF dans le cas de la méthode statique
et UNIQUEMENT dans ce cas là.
8 2950 TG PA 00
Voici la classe Joueur à nouveau modifiée, avec des méthodes statiques pour manipuler
la nouvelle propriété statique :
Classe Joueur
privé :
vie : entier
nom : chaîne
couleur : chaîne
static departvie : entier
mourir()
...
public :
Joueur(lenom : chaîne, lacouleur : chaîne)
static getDepartvie() : entier
static setDepartvie(valdepartvie : entier)
...
Le contenu du getter et du setter est totalement classique. C’est leur utilisation qui est
nouvelle. Imaginons la procédure configJeu qui demande à l’utilisateur de configurer
le jeu en fixant la valeur de départ de la vie pour tous les joueurs. Une fois cette valeur
saisie, elle doit servir à initialiser departvie.
procedure configJeu()
laviedepart : entier
debut
afficher "entrer la vie de départ des joueurs" Pas d’objet nécessaire : Séquence 1
la méthode est appelée
saisir laviedepart
en passant directement
Joueur.setDepartvie(laviedepart) Notions
par la classe.
Fin fondamentales
Les getters et setters statiques, pour influer sur une propriété statique, sont classiques.
Page 19
Cependant, ce ne sont pas les seules méthodes statiques possibles. Imaginons une classe
Date qui permettrait de faire toutes sortes d’opérations sur des objets de type Date
(calculs, comparaisons…). On pourrait facilement avoir une méthode statique aujourd-
hui() qui retournerait la date d’aujourd’hui. Cette date est indépendante des autres
dates, donc son statut static est légitime. Pour obtenir la date d’aujourd’hui, il faudrait
alors écrire :
Date.aujourdhui()
Exercice 5
8 2950 TG PA 00
11. Membre "objet"
Problème
Certaines propriétés peuvent être complexes. Par exemple, chaque joueur a le droit de
choisir une arme. Une arme possède des caractéristiques et des possibilités d’action.
Solution
Une propriété peut être de type objet, donc une instance d’une autre classe. Une
méthode peut aussi retourner un type objet. Ceux sont des membres "objet".
Au niveau de la classe Joueur, voici un nouveau membre "objet" :
Classe Joueur
privé :
vie : entier
nom : chaîne
couleur : chaîne La propriété arme est de
arme : Arme type Arme, donc c’est
... une propriété "objet".
public :
Joueur(lenom : chaîne, lacouleur : chaîne)
combattre()
getArme() : Arme
...
Séquence 1
Cela suppose qu’il y a une nouvelle classe à laquelle il est fait référence :
Notions Classe Arme
fondamentales privé :
nom : chaîne
Page 20 puissance : entier
...
public :
Arme(lenom : chaîne, lapuissance : entier)
utiliser()
ranger()
...
8 2950 TG PA 00
Exercice 6
Exercice 7
Travail à faire
1. Expliquer l’intérêt du mécanisme d’encapsulation et la différence d’utilisation
entre une méthode privée et une méthode publique, dans le contexte de la program-
mation par les objets.
2. À l’aide des méthodes des classes existantes, écrire l’algorithme des méthodes
suivantes :
a) CoûtHoraire de la classe Employé
b) FraisMo de la classe Intervention
c) FraisKm de la classe Intervention
d) Écart de la classe Contrat
8 2950 TG PA 00
Annexe
Intervention = classe
privé
numéro : entier
date : date
durée : entier
tarifkm : réel /* tarif kilométrique retenu */
technicien: Employé /* employé ayant effectué l’intervention */
public
fonction FraisKm(dist : réel) : réel
/* La méthode FraisKm de la classe Intervention calcule les frais
kilométriques occasionnés par une intervention, la distance parcourue
étant passée en paramètre. */
fonction FraisMo() : réel
/* La méthode FraisMo calcule et retourne les frais de main-d’œuvre
occasionnés par une intervention. */
Fin classe Intervention
Employé = classe
privé
numéro : entier
Séquence 1 nom : chaîne
qualification : Grade
Notions
fondamentales dateembauche : chaîne
public
Page 22 fonction CoûtHoraire() : réel
/* La méthode CoûtHoraire détermine et retourne le coût horaire de
l’employé en fonction de sa qualification et de son ancienneté */
Fin classe Employé
Grade = classe
privé
code : caractère
libellé : chaîne
taux : réel
public
fonction TauxHoraire() : réel
/* La méthode TauxHoraire retourne le taux horaire spécifique du grade
*/
debut
retourner taux
fin
Fin classe Grade
8 2950 TG PA 00
Contrat = classe
privé
numéro : entier
date : chaîne
cli : Client
montantcontrat: réel /* montant payé par le client */
tabinterventions : tableau (1:500) de Intervention
nbintervention : entier /* nombre d’interventions réalisées
pour le contrat et présentes dans tabinterventions */
public
fonction Montant() : réel
/* La méthode Montant retourne le montant du contrat payé par le client
*/
debut
retourner montantcontrat
fin
fonction Écart() : réel
/* La méthode Écart détermine et retourne l’écart entre le montant du
contrat et le coût des interventions effectuées sur ce contrat */
Fin classe Contrat
Client = classe
privé
Séquence 1
numéro : entier
nom : chaîne Notions
adresse : chaîne fondamentales
codepos : chaîne
ville : chaîne Page 23
nbkm : entier /* distance du client à la société Kès en km */
public
fonction Distance() : réel
/* La méthode Distance retourne la distance, en kilomètres, qui sépare
le site du client de la société Kès */
debut
retourner nbkm
fin
Fin classe Client
8 2950 TG PA 00
Exercice 8
CLASSE NAVIRE
privé
noLloyds : Chaîne //Numéro du navire
nomNavire : Chaîne // Nom du navire
Séquence 1
libelléFret : Chaîne // Libellé du fret
Notions qtéFret : Entier // Quantité de fret restant à décharger, dite
fondamentales quantité courante
public
Page 24 obtenirQtéFret() : Entier // fonction d’accès sur la donnée qtéFret
décharger(Donnée qté : Entier) // Diminue la qté courante de qté
estDéchargé() : Booléen // indique si la qté courante est nulle ou
non
...
CLASSE STOCKAGE
privé
capaDispo : Entier // capacité de stockage disponible
public
obtenirCapaDispo() : Entier // retourne la capacité restant disponible
stocker(Donnée qté : Entier) // stocke la quantité qté et met à jour
la capacité disponible
estVide() : Booléen // indique si la zone de stockage est vide ou non
estRemplie() : Booléen // indique si la zone de stockage est remplie
ou non
...
CLASSE PORT
privé
tabStock : TABLEAU [1..20] Stockage
public
déchargement (Donnée/Résultat unNavire : Navire)
...
8 2950 TG PA 00
Travail à faire
1. Écrire la méthode estDéchargé de la classe NAVIRE.
2. Écrire la méthode décharger de la classe NAVIRE. On admet que l’utilisation de
la méthode est toujours possible, le contrôle de la quantité de fret disponible étant
fait hors de la méthode.
3. Écrire la méthode déchargement de la classe PORT. Chaque bateau parcourt les
zones de stockage dans l’ordre du tableau tabStock. Par simplification du problème,
on suppose qu’il n’existe qu’un seul fret par navire.
Séquence 1
Notions
fondamentales
Page 25
8 2950 TG PA 00
Synthèse
Classe : Type complexe pouvant regrouper des propriétés, méthodes et/ou événe-
ments. Ce sont les membres de la classe.
Objet : Variable dont le type est une classe. Instancier une classe revient à créer un
objet du type de cette classe.
Encapsulation : Protection de certains membres de la classe. Les membres privés
ne sont accessibles que dans la classe. Les membres publics sont accessibles partout.
Interface d'une classe : Partie publique de la classe, donc partie accessible de l'extérieur.
Séquence 1 Constructeur : Méthode non typée (elle ne retourne rien et n'est même pas typée
en tant que procédure classique), qui porte le même nom que la classe et s'exécute
Notions automatiquement lors de la création de l'objet (lorsque la classe est instanciée avec
fondamentales
"new").
Page 26 Destructeur : Méthode non typée (elle ne retourne rien et n'est même pas typée
en tant que procédure classique), ne possède aucun paramètre, porte le même nom
que la classe précédé du signe ~ et s'exécute automatiquement lors de la destruc-
tion de l'objet.
Membre statique (ou "à portée de classe") : Propriété dont la valeur est iden-
tique pour toutes les instances de la classe (accessible par le nom de la classe si elle
est publique). Méthode publique accessible par le nom de la classe (donc n'ayant
aucune influence sur les propriétés spécifiques à un objet).
Membre "objet" : Propriété dont le type est une autre classe ou méthode qui
retourne un type d'une autre classe.
8 2950 TG PA 00
Séquence 2
Héritage et polymorphisme
Cette nouvelle séquence présente une notion essentielle de la program-
mation objet : l’héritage. C’est à travers cette notion que la puissance de la
programmation objet prend toute sa dimension. Avec l’héritage, il va être
possible de créer des hiérarchies de classes mais aussi d’exploiter des classes
existantes pour les adapter à des besoins spécifiques. Attention, l’héritage
a été à peine abordé dans le module 2944 « Bases de la programmation ».
Cette notion va être nettement plus approfondie dans cette séquence.
X Prérequis
Avoir totalement acquis les connaissances abordées dans la séquence 1.
X Contenu Page 27
1. Héritage simple............................................................................................... 28
2. Héritage et protection.................................................................................... 30
3. Héritage multiple............................................................................................ 32
4. Hiérarchie et vocabulaire ............................................................................... 32
5. Notion de surcharge ....................................................................................... 33
6. Polymorphisme de surcharge ........................................................................ 34
7. Polymorphisme d’héritage............................................................................. 35
8. this / super ...................................................................................................... 36
9. Exercices récapitulatifs ................................................................................... 38
Synthèse ........................................................................... 44
8 2950 TG PA 00
1. Héritage simple
Problème
La classe Arme a été présentée à la fin de la séquence précédente. Elle va donc permettre
d’utiliser des objets de type Arme. Mais un nouveau problème se pose. Il existe 2 types
d’armes : les armes à longue portée qui peuvent tirer, et les armes à courte portée qui
peuvent frapper. Certaines propriétés et méthodes sont communes aux 2 types d’armes,
et d’autres sont spécifiques. Ce serait dommage d’avoir 2 classes complètement distinctes
comportant certains membres en double.
Solution
Il est possible de créer une classe Arme qui contiendra tous les membres communs aux 2
types d’armes, et ensuite de créer 2 classes filles, spécifiques à chaque type d’armes, et
héritant de la classe mère Arme.
classe Arme
Classe mère :
privé : Regroupe les propriétés
nom : chaine et méthodes communes à
puissance : entier toutes les armes.
…
public :
Arme(…)
Séquence 2
ranger()
Héritage …
et polymorphisme
Page 28
Classes filles :
Chaque classe contient des
classe ArmeCourte classe ArmeLongue membres spécifiques.
privé : privé :
… distancemax : entier
public : …
frapper(p : entier) public :
… tirer(d : entier, p : entier)
…
8 2950 TG PA 00
Les classes filles « héritent » des membres non privés de la classe mère : d’où la notion d’héritage. Ce
principe permet d’éviter toute redondance dans la définition des classes.
L’héritage peut se faire sur plusieurs niveaux (une classe fille peut elle-même être mère d’autres
classes).
Notation
En algorithmique, il existe plusieurs notations pour préciser qu’une classe hérite d’une
autre. Il est possible d’utiliser le signe ":" ou d’écrire en toute lettre "hérite de" :
Classe ArmeCourte : Arme
...
(ou)
Classe ArmeCourte hérite de Arme
...
La version textuelle des 3 classes représentées à la page précédente donne donc :
Classe Arme
privé :
nom : chaîne
puissance : entier
...
public :
Arme(lenom : chaîne, lapuissance : entier)
ranger()
...
Séquence 2
Classe ArmeCourte : Arme Les 2 classes filles héritent
privé : de la classe Arme Héritage
et polymorphisme
...
public :
Page 29
frapper(p : entier)
...
Exercice 1
8 2950 TG PA 00
Exercice 2
Est-ce qu’un objet de type ArmeCourte a le droit d’accéder à une méthode publique
de la classe ArmeLongue, puisque les 2 classes ont la même classe mère ?
2. Héritage et protection
Problème
Parfois, certains membres privés de la classe mère doivent être directement accessibles
par les classes filles, sans être accessibles à l’extérieur de la famille. Pour le moment, pour
qu’un membre de la classe mère soit accessible par une de ses classes filles, il faut qu’il
soit public. Mais si le membre est public, tout le monde le voit, même à l’extérieur de la
classe mère et des classes filles.
Solution
Il faut ajouter un troisième niveau de protection : protégé. Un membre qui sera protégé
ne sera accessible que par la classe où il se trouve et les classes filles. Il ne sera pas visible
de l’extérieur.
La classe mère peut donc proposer 3 niveaux de protection :
Séquence 2 Classe Arme
privé :
Héritage
et polymorphisme nom : chaîne
... La propriété puissance est
Page 30 protégé : accessible dans la classe
puissance : entier Arme, mais aussi dans les
classes ArmeCourte et
...
ArmeLongue
public :
Arme(lenom : chaîne, lapuissance : entier)
ranger()
...
Niveaux de protection
Il existe donc 3 niveaux de protection pour les membres d’une classe :
PRIVE : un membre privé n’est accessible que dans la classe où il se trouve.
PROTEGE : un membre protégé n’est accessible que dans la classe où il se trouve et dans
les classes filles de cette classe.
PUBLIC : un membre public est accessible partout.
Exemples d’accès
Exemple d’accès aux différents membres à partir d’une méthode d’une des classes filles
de la classe Arme :
8 2950 TG PA 00
ArmeLongue::tirer(d : entier, p : entier)
debut
puissance est protégé dans la
si d <= distancemax alors classe mère, donc accessible
si p <= puissance alors
lblInfo.titre Å "arme" + nom + "utilisée"
… nom est privé dans la classe
finsi mère, donc il n’est pas accessible
finsi
fin
Exemple d’accès aux différents membres à partir de l’extérieur des classes (l’exemple du
tableau d’objets Joueur est repris, sachant que dans la classe Joueur, il existe un getter
getArme() qui retourne l’arme du joueur) :
debut
… ranger() est une méthode
tJoueur[k].getArme().ranger() publique, donc accessible
… de partout
lblInfo.titre Å tJoueur[k].getArme().puissance puissance est une propriété
… protégée, donc non accessible
tJoueur[k].getArme().frapper(150) de l’extérieur
…
frapper() est une méthode
fin
publique mais dans la classe
ArmeCourte. Elle n’est accessible
que si getArme() retourne un
objet de type ArmeCourte Séquence 2
Héritage
Exercice 3 et polymorphisme
Voici une petite liste d’affirmations pour contrôler vos connaissances sur cette partie Page 31
du cours. Précisez pour chaque affirmation, si elle est vraie ou fausse.
a) Une classe fille peut être mère d’une autre classe. V
b) Une classe peut avoir une seule fille. V
c) Un membre privé est accessible dans la classe où il se trouve et dans les classes
filles qui héritent de cette classe. F
d) Un membre public est accessible à l’extérieur de la classe, mais en passant par
une instance de cette classe. V
e) Un membre public est accessible dans les classes filles de la classe concernée,
mais en passant par une instance de cette classe. F
f) Un membre protégé est accessible à l’extérieur de la classe, mais en passant par
une instance de cette classe. F
g) Un membre protégé est directement accessible dans une des classes filles de la
classe concernée, comme si c’était directement un de ses membres. F
h) Un membre peut être privé et protégé à la fois. F
i) Une méthode est obligatoirement publique. F
j) Une propriété est obligatoirement privée ou protégée. F
k) Un membre peut ne pas avoir de portée (être ni public, ni privé, ni protégé).F
l) Une classe qui n’a pas de filles ne peut pas avoir de membres protégés. F
8 2950 TG PA 00
m) Le constructeur d’une classe fille porte le même nom que le constructeur de
la classe mère. F
n) Une classe fille n’a pas de constructeur puisqu’elle hérite du constructeur de la
classe mère. F
o) Une classe mère accède aux membres publics de ses classes filles. F
3. Héritage multiple
L’héritage multiple est présenté ici à titre anecdotique. Il existe en C++ mais a été sup-
primé, car considéré comme dangereux, en Java. C#, langage qui s’est basé sur de nom-
breux points sur la philosophie Java, ne permet plus l’héritage multiple. Le but est donc
juste de savoir que cela existe. Il n’y aura aucun exercice sur cette notion.
Principe
Une classe fille peut hériter de plusieurs classes mères.
Danger
Il peut y avoir conflit d’héritage si les classes mères possèdent des membres avec des
noms identiques.
Séquence 2
Héritage
et polymorphisme
4. Hiérarchie
Cette partie n’apporte aucune notion supplémentaire, excepté un peu de vocabulaire,
Page 32
qu’il va falloir connaître. Il existe plusieurs termes pour désigner la classe mère, les
classes filles et l’héritage :
Héritage
Dérivation
8 2950 TG PA 00
Surclasse indirecte : classe qui se trouve au moins 2 niveaux au dessus de la classe concer-
née (on ne parle pas de grand-mère, mais c’est pourtant l’idée !).
Sousclasse indirecte : classe qui se trouve au moins 2 niveaux en dessous de la classe
concernée (cette fois, ce serait la petite fille…).
Tout le cours objet comporte beaucoup de vocabulaire. Ne cherchez pas à l’apprendre par cœur : c’est
à force de le manipuler dans les exercices et de relire le cours pour mieux comprendre les notions que
vous allez finir par le connaître.
5. Notion de surcharge
Problème
Par rapport à l’ensemble des armes, l’utilisation d’une arme longue portée nécessite de
préciser la distance de tir. Donc, suivant le type d’arme, l’utilisation ne nécessite pas les
mêmes informations et ne réalise pas les mêmes traitements.
Solution
Il est possible de créer des
classe Arme
méthodes de même nom, une
privé :
dans chaque classe, et avec des
nom : chaine
signatures différentes. Séquence 2
puissance : entier
Cet exemple montre des méthodes
… Héritage
dans 2 classes différentes, cepen-
public : et polymorphisme
dant il est possible de trouver des
Arme(…) Une méthode
méthodes de même nom dans la Page 33
utiliser() pour l’ensemble
même classe. des armes
…
Cet exemple montre aussi des
signatures différentes. Il est pos-
sible d’avoir des méthodes de
même nom et possédant la même
signature. Les parties suivantes
classe ArmeLongue
vont présenter les 2 grands cas
privé :
qui peuvent se présenter.
distancemax : entier
Donc d’une façon plus générale, la
…
surcharge de méthode concerne
public : Une méthode
des méthodes de même nom, soit spécifique aux
utiliser(distance : entier)
dans la même classe, soit dans des armes longues
classes différentes (mère/fille), et …
dont la signature peut être iden-
tique ou différente, mais dont les traitements sont différents.
SURCHARGER UNE MÉTHODE : créer une deuxième méthode du même nom mais ne
jouant pas le même rôle.
Le terme le plus adapté est « polymorphisme ». Pourquoi ce terme ? Parce qu’une même
méthode (ou plutôt un même nom) peut prendre plusieurs formes, et bien sûr, peut
donner plusieurs résultats. Voici donc les deux grandes catégories de polymorphisme.
8 2950 TG PA 00
6. Polymorphisme de surcharge
Problème
Il doit être possible de créer une arme de deux façons différentes :
• soit en initialisant le nom et la puissance ;
• soit sans initialisation (en prévoyant d’initialiser les propriétés plus tard, en utilisant
des setters).
Solution
Il faut créer 2 méthodes de même nom, avec des signatures différentes (ici, des construc-
teurs).
La classe Arme va contenir deux constructeurs :
Classe Arme
privé :
nom : chaîne
...
protégé :
puissance : entier 2 méthodes possédant
... le même nom, mais les
public : paramètres sont différents.
Arme(lenom : chaîne, lapuissance : entier)
Séquence 2
Arme()
Héritage ranger()
et polymorphisme ...
Dans cet exemple, les deux méthodes sont dans la même classe, ce qui est normal
Page 34
puisque c’est le constructeur qui est concerné. Il est possible de trouver ce type de poly-
morphisme sur d’autres méthodes de la même classe, ou de classes différentes (mère/
fille). Dans tous les cas, les méthodes ont des signatures différentes et seront donc repé-
rées par leur signature.
POLYMORPHISME STATIQUE DE SURCHARGE (OVERLOADING) :
• les méthodes ont le même nom (dans la même classe ou dans des classes différentes
de type mère/fille) ;
• les méthodes ont des signatures différentes.
Exercice 4
8 2950 TG PA 00
7. Polymorphisme d’héritage
Problème
Ranger une arme longue ne doit pas comporter les mêmes traitements que pour les
autres armes. Cependant, dans les deux cas, il n’est pas nécessaire de récupérer des infor-
mations supplémentaires, donc les signatures sont identiques.
Solution
Il est possible de créer deux méthodes de même nom et de même signature, l’une dans
la classe mère et l’autre dans la classe fille.
Dans ce type de polymorphisme,
classe Arme
les méthodes ont le même nom
privé :
et la même signature. De ce fait,
nom : chaine
elles ne peuvent pas se trouver
puissance : entier
dans la même classe car il n’y
…
aurait aucun moyen pour les dis-
tinguer. Voilà pourquoi ce type de public :
polymorphisme porte le nom de Arme(…) Une méthode
“polymorphisme d’héritage”. ranger() pour l’ensemble
… des armes
POLYMORPHISME DYNAMIQUE
D’HÉRITAGE (OVERRIDING) :
• les méthodes ont le même Séquence 2
nom et la même signature ;
• les méthodes sont dans des Héritage
classe ArmeLongue et polymorphisme
classes différentes (mère/
fille). privé :
distancemax : entier Page 35
…
public : Une méthode
ranger() spécifique aux
armes longues
…
Exercice 5
8 2950 TG PA 00
8. this / super
Problème
Si les méthodes ont le même nom, comment le programme sait laquelle il doit exécuter
lors d’un appel ?
Solution
1er cas : méthode de même nom et de signature différente.
Æ La reconnaissance se fait par la signature.
2e cas : méthode de même nom et de même signature.
Æ La méthode de la classe concernée est appelée prioritairement.
Dans ces cas là, le programmeur n'a rien à faire de particulier : la reconnaissance est
automatique. Mais il peut y avoir des cas où le programmeur veut aller à l'encontre de
la reconnaissance automatique.
Problème
Comment faire pour accéder à la méthode de la classe mère lorsque la classe concernée
possède une méthode de même nom et de même signature ?
Solution
Sans ajout particulier, ce n’est pas possible. Il faut donc ajouter une précision pour signa-
Séquence 2 ler que la méthode voulue est celle de la classe mère : il faut préfixer la méthode du mot
Héritage
"super.". Exemple : super.ranger()
et polymorphisme super = instance actuelle de la classe mère
Si "super" représente l’instance actuelle de la classe mère, existe-t-il, dans la même
Page 36
logique, un mot qui représente l’instance actuelle de la classe actuelle ?
Oui, ce mot existe, c’est "this".
this = instance actuelle de la classe actuelle
Mais à quoi peut bien servir ce mot ? Encore une fois, il peut servir en cas de situation
ambiguë. Par exemple, il peut être utilisé pour éviter une confusion entre un nom de
paramètre et un nom de propriété privée. Cela va être vu dans le prochain exemple.
L’utilisation de "this" ou "super" est-elle possible lorsqu’il n’y a pas d’ambiguïté ?
Oui : cela permet de repérer immédiatement si le membre est dans la classe actuelle ou
dans la classe mère. Cependant, cette utilisation n’est pas obligatoire.
En algorithmique, d’autres syntaxes existent pour désigner "this" et "super". On peut trouver "self" et
"parent", par exemple. Ces mots sont directement empruntés de différents langages.
8 2950 TG PA 00
Voici deux classes et quelques exemples d’utilisation de "this" et "super" :
classe Arme
privé :
nom : chaine
puissance : entier
…
public :
Arme(nom:chaîne, puissance:entier)
utiliser()
Les méthodes utiliser() et
ranger()
ranger() sont surchargées
… dans la classe fille
classe ArmeLongue
privé :
distancemax : entier
…
public :
utiliser(distance:entier)
ranger()
Séquence 2
…
Héritage
et polymorphisme
Exemple d’utilisation de this et super sur ces classes :
// le constructeur de Arme valorise les propriétés privées Page 37
Arme::Arme(nom : chaine, puissance : num)
debut
this est obligatoire pour accéder
this.nom Å nom aux propriétés privées, car les
this.puissance Å puissance paramètres ont le même nom
fin
8 2950 TG PA 00
9. Exercices récapitulatifs
Comme en fin de séquence précédente, il est temps de vous confronter à des exercices
directement issus de sujets d’examen (même s’ils sont issus de l’ancien BTS).
Attention, le niveau du second exercice (exercice 7) récapitulatif est un peu plus com-
plexe. Vous allez devoir manipuler un vecteur, notion proche de la collection que vous
avez déjà vu dans le module 2944 « Bases de la programmation ». Vous allez aussi mani-
puler un curseur (JeuEnregistrement) déjà abordé dans le module 2946 « Développement
d’applications ». Cependant, les explications données dans le sujet sont suffisamment
claires pour arriver à réaliser le travail sans avoir forcément vu ces classes auparavant. En
prérequis, vous êtes tout de même censé savoir ce qu’est une base de données et savoir
écrire des requêtes SQL simples. Seule la question 3 est concernée par ces notions. Les
autres questions portent directement sur ce qui a été vu dans cette séquence.
Autre remarque importante : vous remarquerez que le constructeur de chaque classe est
nommé, dans ce sujet, "init". Comme cela a été dit dans le cours, il existe effectivement
certains langages qui ne nomment pas le constructeur du même nom que la classe. Cela
ne change rien à la logique du constructeur, excepté que, au lieu d’écrire new suivi du
nom de la classe pour instancier un objet, vous appellerez la methode init sur le nom de
l’objet pour l’instancier.
Exercice 6
Séquence 2 Gestion du stock de pièces détachées (extrait du cas JMS sujet 2002)
L’activité d’entretien des avions implique pour la société JMS la gestion d’un stock
Héritage
et polymorphisme
important de pièces détachées.
Toutes les pièces possèdent un numéro de série. Elles sont rangées chacune dans un
Page 38 casier et accompagnées d’une fiche d’inventaire. Sur chaque fiche d’inventaire, sont
mentionnés le n° de série de la pièce, son libellé (exemples : "anémomètre", "hori-
zon artificiel") ainsi que le nombre d’heures de fonctionnement.
La réglementation impose que les pièces utilisées aient reçu un agrément
"Aéronautique" attesté par un document appelé "JAA FORM ONE" fourni par le
constructeur. Ce document mentionne la date de l’agrément de la pièce.
Cependant, certaines pièces stockées dans le magasin – non essentielles pour la
sécurité – n’ont pas de "JAA FORM ONE". Dans les casiers du magasin, le document
d’agrément est alors remplacé par un ticket.
Ce ticket peut avoir l’une des trois couleurs suivantes :
• VERT : matériel neuf ou réparé.
• ORANGE : matériel à réparer ou à réviser après une certaine période d’utilisation.
• ROUGE : matériel « à rebuter ». On conserve parfois ce matériel à rebuter car il
peut rendre service en dépannage.
On souhaite disposer d’une application permettant de gérer l’état des pièces non
agréées du stock. Cette application sera développée à l’aide d’un langage à objets.
En annexe, les classes nécessaires sont proposées.
Travail à faire
1. Écrire l’algorithme de la méthode REBUTER de la classe Magasin.
2. Écrire l’algorithme de la méthode REVISER de la classe Magasin.
3. Écrire l’algorithme de la méthode SUPPRIMER de la classe Magasin.
8 2950 TG PA 00
Annexe
Seuls les attributs et les méthodes utiles dans le contexte sont présentés.
Dans les fonctions ou procédures, les paramètres sont précédés de :
e pour "entrée",
s pour "sortie" et
es pour "entrée-sortie"
Classe Pièce
Attribut Privé
libelléPièce : Chaîne de caractères
Méthodes Publiques
Fonction getNumSerie() : Chaîne de caractères
Fonction getNbHeures() : Entier
FinClasse
Séquence 2
Classe PièceNonAgréée Hérite de Pièce
Attributs Privés Héritage
etat : Chaîne de caractères et polymorphisme
Méthodes Publiques
Page 39
Procédure setRouge()
Procédure setVert()
Procédure setOrange()
Fonction getEtat() : Chaîne de caractères
FinClasse
Classe Magasin
Attributs Privés
lesPièces[1..10 000] : tableau de PièceNonAgréée
/* Ce tableau contient, pour chaque élément, un objet de la classe
PièceNonAgréée, le tableau est surdimensionné */
nbPièces : Entier
/* nombre réel d’éléments contenus dans le tableau lesPièces */
Méthodes Publiques
Procédure REBUTER (es laPièce : PièceNonAgréée)
/*L’état de la pièce passée en paramètre (de type entrée-sortie) prend
la valeur “ROUGE” */
Procédure RÉVISER (e nbHeures : Entier)
/*permet de passer à “ORANGE” l’état de toutes les pièces non agréées qui
sont à l’état “VERT” et qui ont un nombre d’heures d’utilisation égal ou
supérieur au paramètre passé en entrée */
Fonction SUPPRIMER (e num : Entier ) : Entier
/*permet de supprimer du tableau LesPièces la référence à la pièce dont
le numéro de série est passé en paramètre – voir remarque ci-dessous – */
FinClasse
8 2950 TG PA 00
Remarque sur l’utilisation du tableau lesPièces dans la fonction SUPPRIMER de la
classe Magasin :
Lorsque le numéro de série de la pièce est trouvé dans le tableau, on le supprime
en procédant à un tassement du tableau à partir de cet emplacement jusqu’à la fin
du tableau (donc en décalant le contenu des cases pour ne pas laisser de "trou") ; la
fonction retourne alors l’entier 1. Dans le cas contraire (numéro de série non trouvé),
la fonction retourne l’entier 0.
Exercice 7
Travail à faire :
1. Déclarer et définir le constructeur de la classe FTheorique.
2. En analysant les classes FTechnique et FThéorique héritant de la classe Formation :
a) Justifier le fait d’avoir ou non à redéfinir la méthode afficheFormation() pour
chacune de ces sous-classes.
b) Redéfinir, si nécessaire, pour chaque sous-classe, la méthode afficheFormation().
3. Écrire la méthode chargeLesFormationsTechniques() de la classe CatalogueFormations,
en utilisant notamment la classe JeuEnregistrements décrite en annexe 4B.
4. Écrire la méthode afficheLesFormations() de la classe CatalogueFormations.
8 2950 TG PA 00
Annexe 1 Données utiles à la gestion de la formation continue du personnel
Une formation est soit technique, soit théorique. Il n’existe pas d’autre cas.
Toute formation théorique s’appuie sur un ouvrage de référence identifié par son
numéro ISBN. Les données concernant les formations sont stockées dans une table :
TFORMATION
formId
formDateDeb
formDateFin Remarque : L’attribut formType permet
de savoir si la formation est théorique
formDureeHeures
(formType= "TH") ou technique (form-
formLib Type= "TE").
formNiveau
formISBN
formType
Annexe 2 Description partielles des classes métier pour la gestion des formations
classe Formation
Privé
formId : Entier // Identifiant de la formation
formDateDeb : Date // Date de début de la formation Séquence 2
formDateFin : Date // Date de fin de la formation
Héritage
formDureeHeures : Entier// Durée de la formation en heures et polymorphisme
formLib : Chaîne // Libellé de la formation
formNiveau : Entier // Niveau de la formation Page 41
formType : Chaîne // Type de la formation
Public
/* Constructeur : instancie un objet de la classe Formation et
valorise ses attributs */
procédure init (id : Entier, dateDeb : Date, dateFin : Date, duree :
Entier, lib : Chaîne, niv : Entier, type : Chaîne)
// Autres méthodes
fonction getFormationId () : Entier // Retourne l’identifiant de la
formation
fonction getFormationDateDeb () : Date // Retourne la date de
début de la formation
fonction getFormationDuree () : Entier // Retourne la durée de
la formation en heures
procédure afficheFormation () // Affiche les caractéristiques de la
formation (date de début, durée ...)
8 2950 TG PA 00
classe FTheorique : hérite de Formation
Privé
isbn : Chaîne
Public
// Constructeur – à écrire
classe CatalogueFormations
Privé
lesFormations : Vecteur // Ensemble des formations
Public
// Constructeur
procédure init()
// Ajout d’une formation
procédure ajouteUneFormation (laFormation : Formation)
// Affichage des caractéristiques de toutes les formations – à écrire
procédure afficheLesFormations ()
// Chargement (dans le vecteur) des formations stockées dans la base
de données
procédure chargeLesFormationsTechniques () // À écrire
procédure chargeLesFormationsThéoriques ()
// Recopie (dans les tables de la base) les formations contenues dans
le vecteur
Séquence 2
procédure recopieLesFormations ()
Héritage
et polymorphisme
classe Vecteur
/* Un objet Vecteur est un tableau de taille dynamique. La taille d’un
vecteur grandit au fur et à mesure qu’on y ajoute des éléments. Un vecteur
se manipule exactement comme un tableau (on accède à ses éléments par
leur indice). L’indice du premier élément d’un vecteur est 0 (zéro) */
Public
Fonction taille() : Entier // Nombre d’éléments contenus dans le
vecteur
Procédure ajoute(unElement : ClasseObjet) // Ajouter un élément au
vecteur
Lors de l’utilisation d’un vecteur, le typage des éléments est implicite. Par exemple :
maVariable : Entier
monVecteur : Vecteur
monVecteur[0] Å 10 // Accès à un élément du vecteur
monVecteur[1] Å "uneChaine"
maVariable Å monVecteur[0]
8 2950 TG PA 00
classe JeuEnregistrements
/* Un objet JeuEnregistrements permet de disposer d’un curseur pour
manipuler le résultat d’une requête */
Public
procédure initialiser(s : Chaîne) // Constructeur, reçoit le texte
d’une requête
procédure avancer() // Avance le curseur d’une ligne. Après instanciation
du curseur, cette méthode permet de se placer sur la première ligne
fonction fin() : Booléen // Indique si la marque de fin est atteinte
procédure fermer() // Ferme le curseur et libère les ressources
fonction extraitLigne() : Vecteur // Renvoie un vecteur contenant les
valeurs des champs de la ligne courante
Pour instancier un objet de cette classe, on utilise le constructeur Initialiser
(s : Chaîne) qui réalise la connexion à la base et le chargement en mémoire du jeu
d’enregistrements résultant de l’exécution de l’instruction SQL passée en paramètre.
Par exemple :
Héritage
et polymorphisme
Page 43
8 2950 TG PA 00
Synthèse
Héritage (ou dérivation) : Lien entre 2 classes, dans le but qu’une classe hérite des
membres non privés d’une autre classe.
Classe mère (ou classe de base, ou surclasse) : Classe qui regroupe des membres
communs à plusieurs classes et qui possède une ou plusieurs classes filles.
Classe fille (ou classe dérivée, ou sousclasse) : Classe qui possède des membres
spécifiques et qui hérite d’une autre classe.
Surclasse indirecte : Classe qui, dans une hiérarchie d’héritage, se trouve au moins
2 niveaux au dessus de la classe concernée.
Sousclasse indirecte : Classe qui, dans une hiérarchie d’héritage, se trouve au moins
2 niveaux en dessous de la classe concernée.
Protection : Niveau d’accessibilité d’un membre d’une classe. Il existe 3 niveaux de
protection.
Æ privé : le membre n’est accessible que dans sa classe
Æ protégé : le membre n’est accessible que dans sa classe et dans les classes filles
Séquence 2
Æ public : le membre est accessible partout
Héritage Héritage multiple : Une classe hérite de plusieurs autres classes : cette notion
et polymorphisme
n’existe plus dans les nouveaux langages car considérée à raison comme dange-
reuse.
Page 44
Méthode surchargée : Méthode réécrite avec le même nom, dans la même classe
ou la classe fille.
Polymorphisme statique de surcharge (overloading) : Méthode surchargée avec
une signature différente.
Polymorphisme dynamique d’héritage (overriding) : Méthode surchargée dans la
classe fille, avec la même signature.
this : Instance actuelle de la classe actuelle.
Le "this" permet d’accéder aux membres de la classe dans laquelle on se trouve.
L’utilisation du "this" est obligatoire quand il y a ambiguïté.
super : Instance actuelle de la classe mère.
Le "super" permet d’accéder aux membres non privés de la classe mère. L’utilisation
du "super" est obligatoire quand il y a ambiguïté (en particulier dans le polymor-
phisme dynamique d’héritage).
8 2950 TG PA 00
Séquence 3
Classification, regroupement
et opérations associées
Les notions fondamentales de la programmation objet sont acquises. Il
reste maintenant à voir un ensemble de classifications, les possibilités de
regroupement et quelques opérations associées qui vont compléter les
possibilités déjà offertes. Vous allez, entre autres, découvrir qu’il existe
une classification dans les classes et, surtout, que de nombreuses classes
sont déjà écrites et sont réutilisables à volonté, dans la plupart des lan-
gages, mais aussi récupérables sur internet. Vous allez aussi apprendre à
regrouper les classes.
X Prérequis
Avoir totalement acquis les connaissances abordées dans les séquences précé-
dentes.
Séquence 3
X Capacités attendues en fin de séquence
Classification,
Avoir compris qu’il existe différentes catégories de classes. Savoir manipuler regroupement et
une collection et tout autre type d’objets similaires. Connaître les notions opérations associées
X Contenu
1. Finale (classe, propriété, méthode) ............................................................... 46
2. Abstraite (classe, méthode) ........................................................................... 47
3. Classe métier / classe technique ..................................................................... 48
4. Classe générique : les collections .................................................................. 49
5. Interface (classe purement abstraite) ........................................................... 50
6. Casting............................................................................................................. 54
7. Origine d’un objet .......................................................................................... 56
8. Objet dynamique ............................................................................................ 56
9. Bibliothèque / Package ................................................................................... 57
10. Exercices récapitulatifs ................................................................................... 59
Synthèse ........................................................................... 73
8 2950 TG PA 00
1. Finale (classe, propriété, méthode)
Problèmes
Il est parfois nécessaire d’interdire l’héritage (pour éviter qu’une classe fille puisse nuire
à l’intégrité de la classe mère, par exemple en appelant une méthode de la classe mère
à un moment où il ne faut pas).
Il est parfois nécessaire d’interdire la redéfinition d’une méthode (overriding), en par-
ticulier dans une classe fille (car cette méthode doit obligatoirement réaliser certains
traitements, qui ne seraient alors peut-être pas faits s’il y avait redéfinition).
Il est parfois nécessaire d’interdire la modification d’une propriété (la propriété a alors
un rôle de constante dans la classe).
Solutions
Pour toutes ces interdictions, il suffit de classifier la classe, la méthode ou la propriété
concernée, en « finale ».
classe Arme
privé :
nom : chaine
puissance : entier
…
Séquence 3 public : Interdiction de
Arme(nom:chaîne, puissance:entier) redéfinir la méthode
Classification, utiliser() ranger()
regroupement et finale ranger()
opérations associées
…
Page 46
Méthode interdite
classe ArmeSuperLongue
privé :
…
Classe interdite public :
…
8 2950 TG PA 00
Remarque sur les constantes en programmation objet :
Il existe différents niveaux de constantes.
• propriété finale : constante d’objet (la valeur est fixe pour l’objet concerné) ;
• propriété statique et finale : constante de classe (la valeur est identique pour tous
les objets de la classe et fixe) ;
• propriété publique, statique et finale constante publique de classe (la valeur est
accessible de l’extérieur, identique pour tous les objets de la classe et fixe).
Problèmes
Il est parfois nécessaire d’interdire d’instancier une classe. Par exemple, pour la classe
Arme qui comporte 2 classes filles : ArmeCourte et ArmeLongue, si une arme est obliga-
toirement l’une de ces 2 catégories, seules ces 2 classes doivent être instanciées, et non
la classe mère Arme.
Il est parfois nécessaire de forcer la redéfinition d’une méthode dans toutes les classes
filles. Par exemple, si la classe Arme possède la méthode desactiver() et que toutes les
classes filles doivent désactiver une arme de façon différente, la classe mère doit deman-
der aux classes filles de redéfinir obligatoirement la méthode desactiver().
Séquence 3
Solutions
Classification,
Pour interdire d’instancier une classe, il faut la classifier en "abstraite". regroupement et
Pour forcer la redéfinition d’une méthode dans toutes les classes filles, il faut classifier la opérations associées
Interdiction d'ins-
classe abstraite Arme
tancier cette classe
privé :
(elle n'a d'ailleurs
… pas de constructeur)
Méthode vide qui doit public :
être redéfinie dans abstraite desactiver()
toutes les filles …
8 2950 TG PA 00
3. Classe métier / classe technique
Principe
Il existe deux grandes catégories de classes. Vous avez eu l’occasion de les découvrir
lors des séquences précédentes. Certaines classes comportent des informations : elles
représentent une personne, un objet, un lieu… Ce sont les classes « métiers ». D’autres
classes apportent des outils pour faciliter certaines manipulations : elles permettent de
manipuler les curseurs, les dates… Ceux sont les classes « techniques ».
Classe métier : classe qui permet de décrire un objet qui a une vie propre.
Classe technique : classe « boite à outils » qui permet de faciliter certaines manipula-
tions.
Séquence 3
classe Date Exemple de classe technique
privé : qui offre des outils pour mani-
Classification, … puler des dates (obtenir le
regroupement et public : nombre de jours entre 2 dates,
opérations associées nbJours(uneDate:Date) : entier convertir une date en chaîne…)
convert() : chaîne
Page 48 …
Vous avez tout à fait le droit de créer vos propres classes techniques. Cependant, il existe
aussi de nombreuses classes techniques déjà écrites. Vous les trouverez directement inté-
grées dans les logiciels de développement que vous allez utiliser, ainsi que sur Internet.
Vous pouvez aussi créer des classes techniques en héritant de classes techniques exis-
tantes. Cela permet parfois d’avoir une classe technique offrant des outils complémen-
taires par rapport à la classe technique d’origine. Voilà pourquoi, lorsque vous récupérez
une classe technique, vous avez la déclaration détaillée de la classe (non pas le contenu
du code, mais la liste des membres protégés et publiques, donc tout ce que vous avez
le droit d’utiliser dans une classe fille). Imaginons par exemple que vous avez besoin de
connaître le nombre de semaines entre 2 dates. La classe Date existante n’offre que le
nombre de jours. Vous pouvez alors créer une classe MaClasseDate qui hérite de Date et
qui contiendrait la méthode nbsemaines ainsi définie :
MaClasseDate::nbsemaines(uneDate : Date) : réel
debut
retourner (super.nbjours(uneDate) / 7) //super est optionnel
fin
8 2950 TG PA 00
4. Classe générique : les collections
Problème
Depuis le début de ce cours, pour manipuler un ensemble d’objets, vous avez essentiel-
lement utilisé des tableaux. Vous avez déjà eu l’occasion d’utiliser les collections dans le
module 2944 « Bases de la programmation », cependant revoyons cette notion.
Rappelez-vous que le tableau présente plusieurs inconvénients :
• il est contigu en mémoire (donc il faut prévoir sa place à l’avance) ;
• il est de taille fixe (donc il faut prévoir le nombre maximum de cases à l’avance) ;
• il n’est pas pratique à modifier (l’insertion et la suppression doivent être gérées avec
des décalages pour faire de la place ou éviter un « trou »).
Solution
Parmi les classes techniques, il existe un ensemble de classes spécialement dédiées à la
manipulation d’un ensemble d’objets. Ces classes, appelées Collections, permettent de
manipuler des ensembles d’objets avec les caractéristiques suivantes :
• les objets ne sont pas placés de façon contiguë dans la mémoire ;
• le programmeur ne s’occupe pas de la façon dont sont mémorisés les objets ;
• la taille de la collection est variable et s’adapte au nombre d’objets qu’elle contient ;
• l’ajout et la suppression d’objets ne nécessitent aucun décalage ;
• l’accès à un objet peut se faire séquentiellement ou par indice, voire par une clé Séquence 3
prédéfinie (suivant le type de collection) ;
Classification,
• certaines collections offrent des possibilités supplémentaires, comme la recherche, regroupement et
le tri… opérations associées
8 2950 TG PA 00
Voici un exemple d’utilisation de la classe Collection dont l’interface est donnée ci-des-
sus. L’extrait de programme est extérieur à toute classe :
...
k : entier
joueur : Joueur
col : Collection <Joueur>
debut
...
// création de la collection d’objets de type Joueur
col Å new Collection <Joueur>()
// création d’un objet de type Joueur
joueur Å new Joueur("Ed", "noir")
// ajout de cet objet à la collection
col.Add(joueur)
...
// le kème joueur de la collection utilise la méthode frapper
col.Valeur(k).frapper()
... col.Valeur(k) retourne le kème objet de la
Fin collection, donc un objet de type Joueur
Il est possible d’éviter de passer par un objet intermédiaire pour ajouter un objet à une
collection :
Séquence 3
Remplacer :
Classification, // création d’un objet de type Joueur
regroupement et joueur Å new Joueur("Ed", "noir")
opérations associées
// ajout de cet objet à la collection
col.Add(joueur)
Page 50
Par :
// création de l’objet de type Joueur et ajout dans col
col.Add(new Joueur("Ed", "noir"))
Principe
Le langage Java offre un type de classe un peu particulier que l’on appelle Interface. Une
interface est une classe abstraite dont toutes les propriétés sont des constantes de classe
(publiques, statiques et finales), et toutes les méthodes sont par défaut abstraites (donc
à redéfinir dans les classes qui « héritent » de cette interface). On retrouve la notion
d’interface aussi en C# qui s’est inspiré de Java. Dans d’autres langages, la notion d’inter-
face est parfois aussi présente mais avec des variantes et un autre nom.
8 2950 TG PA 00
Intérêt
Dans le cadre du TP, vous allez utiliser une interface uniquement pour mémoriser les
constantes globales à toute l’application. Cette interface ne contiendra donc que des
propriétés.
Une interface peut aussi contenir des méthodes abstraites afin de servir de modèle pour
la création d’autres classes.
Notion d’implémentation
Une classe n’hérite pas d’une interface : elle l’implémente. Implémenter une interface
suppose respecter le modèle qu’elle impose (à travers ses méthodes abstraites) et permet
d’accéder à ses constantes. Contrairement à l’héritage qui doit être unique (excepté en
C++), l’implémentation peut être multiple : une classe peut implémenter plusieurs inter-
faces, donc respecter plusieurs modèles.
Une interface est par contre autorisée à hériter de plusieurs autres interfaces.
Exemple
Voici un petit exemple simpliste et très classique pour expliquer ce qu’est une interface.
Imaginons que vous vouliez créer une application qui permet, entre autre, de gérer des
formes géométriques planes avec la possibilité, pour chaque forme, de calculer l’aire
et le périmètre. Les calculs sont différents suivant le type de forme, par contre chaque
forme a forcément une aire et un périmètre.
Le programme doit ensuite pouvoir manipuler n’importe quel type de forme, pour- Séquence 3
quoi pas dans une collection, et accéder à l’aire et au périmètre de chaque forme. Les Classification,
méthodes pour y accéder doivent avoir le même prototype (même nom, même signa- regroupement et
ture) dans toutes les classes. Donc il faut forcer toutes les classes qui décrivent une forme opérations associées
précise à posséder ces 2 méthodes.
Page 51
Voici la solution :
interface Forme
public :
// ces méthodes sont abstraites
aire() : réel
perimetre() : réel
8 2950 TG PA 00
Vous remarquez que les 2 classes Carre et Cercle implémentent l’interface Forme qui ne
contient que 2 méthode abstraites. Du coup, les 2 classes sont obligées de redéfinir ces
2 méthodes. Mais leur contenu sera différent.
Voici le contenu des 2 méthodes redéfinies dans la classe Carre :
// calcul de l’aire
Carre::aire() : réel
debut
retourner (cote * cote)
fin
// calcul du périmètre
Carre::perimetre() : réel
debut
retourner (4*cote)
fin
Voici le contenu des 2 méthodes redéfinies dans la classe Cercle :
// calcul de l’aire
Cercle::aire() : réel
debut
retourner (Math.PI * rayon * rayon)
fin
// calcul du périmètre
Cercle::perimetre() : réel
debut
Séquence 3 retourner (2 * Math.PI * rayon)
fin
Classification,
regroupement et
opérations associées Dans cet exemple, on est parti du principe qu’on avait à disposition la classe Math avec
la propriété publique statique PI.
Page 52 Vous remarquez donc que les contenus des méthodes sont bien différents. En revanche,
les méthodes ont bien le même nom et la même signature. Cette implémentation nous
garantit que toutes les classes concernées contiennent forcément ces méthodes.
Du coup, si vous créez une collection basée sur l’interface Forme, vous allez pouvoir
accéder aux aires et périmètres de tous les objets de la collection sans vous inquiéter du
type de chaque objet.
// déclaration de la collection
colForme : Collection <Forme>
// un objet pour récupérer les objets de la collection
uneForme : Forme
debut
// création de la collection
colForme Å new Collection <Forme>()
...
// exemple d’ajout d’un objet dans la collection
colForme.Add(new Carre(50))
...
// affichage des aires de chaque objet de la collection
pour chaque uneForme dans Forme
// uneForme est du type de l’objet récupéré (Carre ou Cercle)
afficher uneForme.aire()
finpour
...
fin
8 2950 TG PA 00
Exercice 1
Voici quelques déclarations (incomplètes) :
interface IFA
public :
a()
interface IFB
public :
b()
interface IFD
public :
d()
8 2950 TG PA 00
Voici des contenus proposés pour la méthode f() de la classe CAlpha. Pour chaque
contenu, dites s’il est valide et expliquez pourquoi.
8. this.b()
9. this.f()
10. alpha.f()
11. alpha.a()
12. a()
this.d()
13. beta.f()
Pour chacune des lignes suivantes (qui sont des déclarations + instanciations écrites
en Java, possédant la même syntaxe que le C++, donc avec le type en premier), dites
si elle est valide et expliquez pourquoi.
Classification,
regroupement et
opérations associées
Page 54
6. Casting
Problème
Il est parfois nécessaire de modifier le type d’un objet, comme cela arrive aussi pour cer-
taines variables simples. Par exemple, imaginons que dans le jeu en client/serveur, il soit
nécessaire de transférer des objets (de différents types : Joueur, Arme…). Ce serait dom-
mage de faire une procédure de transfert différente pour chaque type d’objet. Donc,
plutôt que de transférer un objet de type Joueur, ou Arme, on va écrire une procédure
plus générale, qui va transférer des objets de type Object (c’est le type générique, dont
toutes les autres classes héritent). À la réception, l’objet reçu, de type Object, doit pou-
voir être transformé de telle sorte qu’il redevienne un Joueur ou une Arme.
Solution
Le casting (ou « transtypage ») permet de changer le type d’un objet, à condition qu’il
y ait compatibilité : quel que soit le type actuel de l’objet, son vrai type d’origine doit
être le type du casting (ou éventuellement un type intermédiaire entre le type actuel et
le type d’origine).
8 2950 TG PA 00
Voici un schéma pour mieux comprendre le fonctionnement (commencez par la gauche) :
classe Object
objet monObjet monObjet est de type Object,
nom : fusil mais c'est tout de même, à
Pas de casting pour puissance : 40 l'origine, un objet Arme.
"sur-typer" un objet. …
Le casting est
nécessaire pour
classe Arme classe Arme "sous-typer"
objet monArme objet uneArme l'objet (et n'est
nom : fusil nom : fusil possible que
puissance : 40 puissance : 40 parce qu'à
… … l'origine,
monObjet était un
objet Arme).
Imaginons la procédure recupArme, qui reçoit un objet de type Object (objet qui par
exemple a été transmis par le réseau, et qui était à l’origine un objet de type Arme) et
qui doit valoriser l’arme du joueur avec cet objet :
procedure recupArme (unObjet : Object, unJoueur : Joueur)
uneArme : Arme
debut
Impossible car setArme attend un objet de
unJoueur.setArme(unObjet) type Arme, et unObjet est de type Object
uneArme Å (Arme) unObjet
Il faut d’abord caster unObjet pour le
unJoueur.setArme(uneArme)
transformer en Arme
fin
8 2950 TG PA 00
7. Origine d’un objet
Problème
Il est parfois nécessaire de connaître le type d’un objet. Par exemple, dans une collection
d’armes, il peut y avoir des objets de type ArmeLongue et d’autres de types ArmeCourte.
Si je veux parcourir la collection et utiliser la méthode tirer() sur toutes les armes longues
de la collection, il faut savoir les repérer.
Solution
Il est toujours possible de connaître le type d’un objet. Cette reconnaissance peut
prendre différentes formes suivant les langages.
Origine d’un objet = classe dont l’objet est l’instance.
Voici la syntaxe algorithmique la plus utilisée, mais en réalité, suivant les sujets, il peut
être suggéré une autre méthode :
si monObjet est ArmeLongue alors
monObjet.tirer()
finsi
// syntaxe en C#
Séquence 3
if (monObjet is ArmeLongue) ...
Classification, // syntaxe en Java
regroupement et if (monObjet instanceof ArmeLongue) ...
opérations associées
Page 56 Exercice 2
8. Objet dynamique
Une variable dynamique est une variable dont l’accès se fait directement par son adresse,
mémorisée dans une variable appelée pointeur. Ainsi, l’accès à la variable dynamique
se fait en passant par son pointeur. L’avantage principal d’une variable dynamique est
8 2950 TG PA 00
qu’elle peut être détruite à tout moment. En effet, une variable classique n’est « libé-
rée » qu’à la sortie du bloc dans lequel elle a été déclarée : le développeur ne peut donc
pas décider de la libérer quand il le souhaite.
Comme pour n’importe quelle autre variable, il est tout à fait possible de déclarer et
utiliser un objet dynamique, donc en y accédant par son adresse.
Intérêt
Comme toute variable dynamique, son intérêt réside avant tout dans sa libération : à
tout moment, lorsque l’objet n’est plus utile, il est possible de libérer sa place mémoire.
Cet intérêt parait d’autant plus évident que la création (avec new) d’un nouvel objet,
impose la réservation d’un espace mémoire pour cet objet. Cet espace n’est pas libéré
automatiquement. Du coup, le fait que l’objet soit dynamique permet sa libération à
tout moment.
Il n’est du coup plus trop opportun d’utiliser des variables dynamiques pour manipuler Classification,
les objets. D’ailleurs, compte tenu de la dangerosité de la manipulation de variables regroupement et
dynamiques, certains langages, comme le Java, ont supprimé cette possibilité. opérations associées
Conclusion Page 57
Vous n’utiliserez pas de variables dynamiques pour manipuler des objets. Cependant il
fallait en parler, pour que vous sachiez d’abord que cela existe, et ensuite que cette pra-
tique se retrouve encore parfois dans un langage comme le C++ (qui reste le langage le
plus proche de la machine, le plus puissant et du coup, le plus dangereux).
9. Bibliothèque / Package
8 2950 TG PA 00
Bibliothèque = regroupement de classes et/ou bibliothèques.
Une bibliothèque peut très bien contenir en même temps des classes et d’autres biblio-
thèques.
Imaginons l’organisation des classes du jeu de combat 2D dont il est question depuis le
début de ce cours. Les bibliothèques et une partie des classes sont ainsi hiérarchisées :
Bibliothèque
Jeu2D
Bibliothèque Bibliothèque
Métier Technique
Page 58 À vous de trouver la hiérarchie la plus adaptée à votre application. La hiérarchie permet aussi de
créer des bibliothèques réutilisables dans d’autres applications (par exemple, ici la bibliothèque
ClientServeur qui pourrait servir à réaliser des transferts dans d’autres applications).
8 2950 TG PA 00
ne correspondent pas exactement à ce que vous voulez : vous pouvez alors créer des
classes filles pour personnaliser certaines fonctionnalités.
Comment utiliser une bibliothèque existante ?
Pour utiliser une ou plusieurs classes existantes, vous devez intégrer la classe, ou la biblio-
thèque complète, dans votre application. Cela se fait en utilisant un ordre d’intégration
dès le début du programme.
Voici quelques exemples dans différents langages :
// syntaxe en C#
using Jeu2D.Technique.ClientServeur ;
// syntaxe en Java
import Jeu2D.Technique.ClientServeur ;
// syntaxe en C++
#include "ClientServeur.h" ;
// syntaxe en PHP
include ("ClientServeur.php") ;
8 2950 TG PA 00
Exercice 3
Séquence 3
Classification,
regroupement et
opérations associées
Page 60
8 2950 TG PA 00
Annexe 1 Déclaration des classes métiers
Classe Oeuvre
Attributs privés
numéroOeuvre : Entier
titreOeuvre : Chaîne
Méthodes publiques
procédure init (unNuméro : Entier, unTitre : Chaîne)
// initialise les attributs avec les valeurs passées en paramètre.
fonction getNuméro( ) : Entier // retourne l’attribut numéroOeuvre
fonction getTitre ( ) : Chaîne // retourne l’attribut titreOeuvre
8 2950 TG PA 00
Annexe 2 Classe technique Collection
Classe Collection
// Il s’agit d’une classe générique.
Méthodes publiques
fonction cardinal() : Entier
// retourne le nombre d’éléments de la collection
fonction existe(unObjet : Objet) : Booléen
// teste si unObjet fait partie de la collection
fonction index(unObjet : Objet) : Entier
// retourne l’index de l’objet passé en paramètre dans la
// collection, le premier objet de la collection a pour index 1
fonction donnerObjet(unIndex : Entier) : Objet
// retourne l’objet qui se trouve à l’index passé en paramètre
procédure ajouterObjet(unObjet : Objet)
// ajoute unObjet à la collection
procédure remplacerObjet(unIndex : Entier, unObjet : Objet)
// remplace, dans la collection, l’objet figurant à l’index passé en
// paramètre par l’objet passé en paramètre
procédure retirerObjet(unObjet : Objet)
// supprime de la collection l’objet passé en paramètre
procédure vider()
Séquence 3 // vide le contenu de la collection
Classification,
regroupement et // Pour instancier une collection
opérations associées uneCollection : Collection de MaClasse
// la collection instanciée contiendra des objets de la classe MaClasse
Page 62
8 2950 TG PA 00
Exercice 4
Boîte à outils
z Terrasse
Étalage
Séquence 3
La mairie souhaite pouvoir visualiser sur écran les plans des quartiers avec les diffé-
Classification,
rents emplacements loués, selon le modèle de la figure ci-dessous. Une application regroupement et
qui sera développée à l’aide d’un langage à objets est en cours de conception. opérations associées
Les plans des quartiers sont stockés dans des fichiers images de type matriciel (bit-
map). On distingue une terrasse par un rond vert et un étalage par un carré rouge. Page 63
Ces deux symboles sont de dimension fixe.
Pour créer un nouvel emplacement, l’utilisateur clique sur l’objet de la boîte à outils
souhaité (Terrasse ou Étalage) et le fait glisser sur le plan : une nouvelle instance
de cet objet est alors créée, ses coordonnées sont mémorisées et un écran de saisie
s’affiche.
Vous trouverez une partie des classes définies pour réaliser cette application en
annexe. On s’intéresse à l’enregistrement d’un nouvel emplacement sur le plan.
Travail à faire
1. Expliquer pourquoi l’attribut dimension de la classe Emplacement est un attribut
à portée de classe.
2. Nommer et expliquer le (ou les) mécanisme(s) mis en œuvre pour la déclaration de
la méthode affiche() dans les classes Terrasse et Etalage.
3. Écrire la méthode supprimeEmplacement de la classe Plan.
La méthode ajouteEmplacement a pour rôle d’ajouter un nouvel emplacement sur le
plan. Cet ajout ne sera toutefois pas possible si l’emplacement est une terrasse située
à moins de 50 mètres d’une autre terrasse. On suppose que l’emplacement à ajouter
n’existe pas déjà dans la collection des emplacements rattachés au plan.
4. Écrire la méthode ajouteEmplacement de la classe Plan.
8 2950 TG PA 00
Annexe Les classes
Dans les fonctions ou procédures, les paramètres sont précédés de e pour "entrée",
classe Plan
// Attribut privé à portée classe
echelle : Entier
// Attributs privés
nomFichier : Chaîne
lesEmplacements : Collection de Emplacement
// Mémorise tous les emplacements placés sur le plan (terrasse ou étalage)
// Méthodes publiques
fonction ajouteEmplacement(e unEmplacement : Emplacement) : Booléen
// Ajoute le nouvel emplacement (terrasse ou étalage) dans la collection
lesEmplacements. Elle renvoie une valeur booléenne permettant de savoir
si l’ajout a été possible.
procédure supprimeEmplacement(e unEmplacement : Emplacement)
// supprime l’emplacement de la collection lesEmplacements. Elle vérifie
au préalable l’existence de l’emplacement dans la collection.
procédure affichePlan()
// Affiche l’image bitmap du plan avec les emplacements (terrasse et
étalage). On distingue une terrasse par un rond vert et un étalage par un
carré rouge.
Séquence 3
procédure sauvegardePlan()
Classification, // Sauvegarde le plan et ses emplacements dans le fichier
regroupement et Fin classe Plan
opérations associées
classe Emplacement
Page 64
8 2950 TG PA 00
classe Terrasse hérite de Emplacement
// Méthode publique
procédure affiche() // Affiche un cercle
fin classe Terrasse
L’implémentation de la classe Plan utilise une classe technique Collection qui offre des
services pour la gestion d’un ensemble d’objets.
classe Collection
// Méthodes publiques
Fonction cardinal() : Entier
// Renvoie le nombre d’éléments de la collection
Fonction existe(e unObjet : Objet) : Booléen
// Teste si unObjet existe dans la collection
Fonction index(e unObjet : Objet) : Entier // Renvoie l’index de unObjet,
le premier objet de la collection a pour index 1
Fonction extraireObjet(e unIndex : Entier) : Objet Séquence 3
// Retourne l’objet d’index unIndex
Procédure ajouter(e unObjet : Objet) // Ajoute un objet à la collection Classification,
regroupement et
Procédure enlever(e unIndex : Entier) // Supprime l’objet d’index unIndex
opérations associées
de la collection
Procédure vider() // Vide le contenu de la collection Page 65
Fin classe Collection
Exercice 5
8 2950 TG PA 00
Les règles sont les suivantes :
Métropole hors Corse : chaîne de 2 chiffres de "01" à "19" et de "21" à "95"
Corse : "2A" et "2B"
DOM-TOM : chaîne de 3 chiffres : de "971" à "976"
Travail à faire
1. Écrire la fonction localisation de la classe Abonnement. On supposera que le code
postal a été contrôlé préalablement.
2. Écrire la fonction prixAbonnement de la classe Abonnement.
Travail à faire
5. Écrire la fonction relancerAbonnements de la classe Revue.
8 2950 TG PA 00
Annexe 1 Description des classes définies pour la gestion des abonnements
Classe Revue
Attributs privés :
code : Chaîne // code de la revue
titre : Chaîne // titre de la revue
prixAbonnementPublic : Réel // prix public de l’abonnement à la revue
lesAbonnements : Collection de Abonnement
Méthodes publiques :
fonction getPrixAbonnementPublic() : Réel
// retourne le prix public de l’abonnement à la revue
fonction relancerAbonnements() : Collection de Abonnement
/* retourne une collection d’objets de la classe Abonnement regroupant les
abonnements nécessitant un processus de relance pour un renouvellement */
Classe Abonnement
Attributs privés :
numéro : Entier // numéro d’abonnement
nom : Chaîne // nom de l’abonné Séquence 3
prénom : Chaîne // prénom de l’abonné
Classification,
raisonSociale : Chaîne // raison sociale de l’abonné regroupement et
adresse : Chaîne // adresse d’abonnement opérations associées
codePostal : Chaîne // code postal d’abonnement
ville : Chaîne // ville d’abonnement Page 67
dateDébut : Date // date de début de l’abonnement
dateFin : Date // date de fin de l’abonnement
laRevue : Revue // objet Revue auquel est rattaché l’abonnement
Méthodes publiques :
procédure init(unNuméro : Entier, unNom : Chaîne, unPrénom : Chaîne,
uneRaisonSociale : Chaîne, uneAdresse : Chaîne, unCodePostal : Chaîne,
uneVille : Chaîne, uneDateDébut : Date, uneDateFin : Date, uneRevue :
Revue)
// valorise les attributs privés
fonction localisation() : Caractère
// retourne "M" si le département est en métropole, "D" s’il est dans
les DOM-TOM
fonction getDateFin( ) : Date
// permet d’obtenir la date de fin d’un abonnement
fonction prixAbonnement() : Réel
// retourne le prix de l’abonnement en fonction du prix public de la
revue de l’abonnement et de la localisation de l’abonnement (métropole ou
DOM-TOM)
8 2950 TG PA 00
Annexe 2 Description textuelle des classes techniques Collection, Date et Chaîne
Classe Collection
/* Classe générique : Un objet de la classe Collection permet de gérer un
ensemble d’objets de même nature. Cette classe affranchit le développeur
de la gestion du dimensionnement, contrairement à un tableau. */
Méthodes publiques
fonction cardinal() : Entier
// retourne le nombre d’éléments de la collection
fonction existe(unObjet : Objet) : Booléen
// teste si unObjet fait partie de la collection
8 2950 TG PA 00
Classe Date
Attributs privés :
année : Entier
mois : Entier
jour : Entier
Méthodes publiques :
fonction année() : Entier // renvoie l’année
fonction mois() : Entier // renvoie le mois
fonction jour() : Entier // renvoie le jour
fonction différence(uneDate : Date ) : Entier
/* renvoie le nombre de jours de différence entre l’objet Date courant
et le paramètre uneDate. Si l’objet Date courant correspond à une date
antérieure au paramètre uneDate, le nombre de jours retourné est positif.
Dans le cas contraire, le nombre de jours retourné est négatif. */
8 2950 TG PA 00
Exercice 6
Travail à faire
1. Écrire la méthode margeBruteCourante() de la classe Projet.
2. Écrire la méthode cumulCoûtMO() de la classe Projet.
3. Écrire la méthode nbHeuresEffectuées() de la classe Mission.
Séquence 3
Classification,
regroupement et
opérations associées
Page 70
8 2950 TG PA 00
Annexe 1 Descriptif des classes métiers
Classe Projet
privé :
nom : chaîne
début : Date
fin : Date
prixFacturéMO : réel
// prix auquel le projet a été vendu sur le chapitre main d’œuvre
missions : Collection de Mission
Fonction cumulCoûtMO() : réel
// retourne le coût cumulé des heures de main-d’œuvre effectuées pour
Séquence 3
l’ensemble des missions du projet
public : Classification,
Fonction margeBruteCourante() : réel regroupement et
// retourne la différence entre le prix facturé au chapitre main-d’œuvre opérations associées
et le coût des heures de main-d’œuvre effectuées pour l’ensemble des
missions du projet Page 71
finClasse
Classe Mission
privé :
nom : chaîne
description : chaîne
nbHeuresPrévues : entier
// nombre d’heures prévues pour réaliser la mission
relevéHoraire : Dictionnaire de (Date,entier)
// nombre d’heures passées par jour par la personne chargée d’exécuter
cette mission
exécutant : Intervenant
// personne chargée d’exécuter la mission
public :
Fonction getRelevéHoraire() : Dictionnaire de (Date, entier)
// accesseur sur l’attribut relevéHoraire
Fonction getExécutant() : Intervenant
// accesseur sur l’attribut exécutant
Procédure ajoutRelevé(jour : Date, nbHeures : entier)
// ajoute au relevé, une date et un nombre d’heures
Fonction nbHeuresEffectuées() : entier
// retourne le nombre d’heures réellement effectuées du relevé horaire
finClasse
8 2950 TG PA 00
Annexe 2 Descriptif des classes techniques
Public :
procédure ajouter(clé : typeClé, valeur : typeValeur)
// ajoute un élément (clé, valeur) dont le premier paramètre est la clé et
le second paramètre la valeur. Remplace la valeur de l’élément si cette
clé est déjà présente.
fonction donnerValeur(clé : typeClé) : typeValeur
// accède à la valeur dont la clé est passée en paramètre.
fonction donnerToutesLesClés() : Collection de typeClé
// retourne une collection contenant toutes les clés du dictionnaire.
procédure retirer(clé : typeClé)
// retire du dictionnaire un élément dont la clé est fournie en paramètre
;ne fait rien si cette clé n’est pas présente
fonction existe(clé : typeClé) : booléen
// retourne vrai si l’élément dont la clé est passée en paramètre est
présent dans le dictionnaire
Fin classe
8 2950 TG PA 00
Exemple d’utilisation du dictionnaire :
unDico : Dictionnaire de (Date,entier)
nbHeures : entier
dateJ : Date
unDico Ånew Dictionnaire de (Date,entier)
…
nbHeures Å unDico.donnerValeur(dateJ)
Synthèse
Séquence 3
Propriété finale : Propriété dont le contenu ne peut être modifié (constante). Page 73
Classe abstraite : Classe qui ne peut être instanciée.
Méthode abstraite : Méthode vide qui doit être redéfinie dans toutes les filles, avec
la même signature.
Classe métier : Classe qui décrit un objet réel (personne, lieu, objet, etc.).
Classe technique : Classe « boite à outils » qui offre des facilités de manipulations.
Interface Java : Classe abstraite ne contenant que des constantes de classe et des
méthodes statiques (une classe peut inplémenter plusieurs interfaces).
Casting : Changement de type d’un objet (à condition que l’objet soit d’un type
compatible) :
objet_receveur Å (nouveau_type) objet_à_caster
8 2950 TG PA 00
Séquence 4
Persistance des objets
Les séquences précédentes ont fait le tour des notions fondamentales liées
à l’utilisation des objets dans une application. Le point qui reste à aborder
touche le domaine de la sauvegarde de ces objets. Cette notion représente
souvent un problème stratégique important et nécessite de faire des choix
pertinents, voire d’apporter des modifications à l’application de départ.
Vous allez découvrir les principales techniques de sauvegardes qui s’of-
frent actuellement dans ce domaine.
X Prérequis
Avoir acquis les connaissances abordées dans toutes les séquences précédentes.
Séquence 4
X Contenu
Persistance
1. Qu’est-ce qu’une persistance d’objets ? ....................................................... 76 des objets
Synthèse ........................................................................... 86
8 2950 TG PA 00
1. Qu’est-ce que la persistance d’objets ?
Problème
Les objets sont des variables, donc avec une durée de vie limitée : une variable est libérée
dès que le bloc de code, à l’origine de sa déclaration, se termine. Parfois il serait intéres-
sant de pouvoir mémoriser le contenu des objets pour une utilisation ultérieure.
Pour reprendre l’exemple du cours sur le jeu de combat 2D, si vous voulez, pour chaque
joueur, cumuler ses scores et gérer un classement entre les joueurs, cela suppose que les
scores doivent être mémorisés d’une partie sur l’autre. Même problème si le joueur doit
pouvoir, en tapant son pseudo, retrouver son personnage à chaque partie.
Solution
La mémorisation d’objets est possible. Il existe différentes techniques pour mémoriser le
contenu d’objets. Chaque technique a ses avantages et ses inconvénients. Il faut donc
trouver la solution la plus adaptée aux besoins. On parle de persistance des objets pour
mettre en avant le besoin d’allonger la durée de vie d’un objet pour une utilisation ulté-
rieure : pour que son contenu « persiste » au-delà de sa durée de vie classique.
Persistance d’un objet = sauvegarde d’un objet.
Sauver unJoueur consiste à mémoriser son nom ("Ed"), sa couleur ("Noir") mais aussi le
contenu de sa propriété arme qui est un objet de type Arme. Donc cet objet doit aussi
être sauvé avec son nom ("epée"), sa distance (2), sa puissance (35), etc…
8 2950 TG PA 00
Cas de l’héritage
Sauver le contenu d’un objet qui est une instance d’une classe fille, suppose sauver aussi
les propriétés de la classe mère.
L’intérêt de mémoriser les objets dans une base de données réside essentiellement dans
Page 77
l’étendue des possibilités d’exploitation. Les données créées par le programme pourront
être exploitées par d’autres programmes et pourront être gérées avec le SQL. C’est cer-
tainement cet aspect, et la popularité des bases de données relationnelles, qui rend cette
solution si répandue.
La sérialisation
C’est la solution la plus naturelle. Elle consiste à créer un fichier et, en passant par des
instructions simples de sérialisation, à mémoriser le contenu des propriétés d’un objet
dans le fichier, sous un format binaire, non lisible en dehors du programme.
Pour mettre en œuvre cette solution, il suffit de préciser pour chaque classe concernée,
si elle est sérialisable (en ajoutant un ordre au dessus du nom de la classe). À partir de là,
à travers une petite classe de sérialisation et désérialisation, la sauvegarde et la récupé-
ration d’un objet se font en une ligne de code. Si le programme contient une collection
d’objets, sérialiser la collection va permettre de sérialiser l’ensemble des objets de la
collection, ce qui est très pratique et puissant.
Quelques problèmes sont tout de même liés à cette solution :
• tout n’est pas sérialisable : si l’objet à sérialiser contient des propriétés de type
objet, provenant de classes existantes (comme la classe Image) qui ne sont pas séria-
lisables, alors l’objet de départ ne sera pas sérialisable ;
• Les objets sérialisés ne sont exploitables que par le programme qui les a sérialisé.
8 2950 TG PA 00
Le fichier XML
C’est une solution qui devient de plus en plus à la mode. Le format XML est le format
standard et universel de stockage d’informations sur internet, mais pas seulement. Le
format XML est aussi très utilisé pour mémoriser des données de type configuration.
La plupart des langages offrent des " parseurs XML " qui sont des outils permettant de
manipuler simplement les fichiers XML. En exploitant ces outils, la manipulation d’un
fichier XML devient aussi simple que la manipulation d’une base de données.
Par rapport à une base de données, le fichier XML offre la légèreté (c’est un simple
fichier texte), l’universalité et l’absence de logiciel intermédiaire pour l’exploiter.
Cependant, même si plusieurs applications différentes peuvent l’exploiter, le fichier XML
n’offre aucune sécurité ni de gestion de verrou pour accéder à ses informations. Donc,
contrairement à une base de données, il est incapable de gérer des accès concurrents :
plusieurs accès en même temps sur les mêmes informations.
Technique Passer par un Passer par la Passer par une Passer par un curseur
curseur sérialisation classe XML
Pour pallier cette diversité de solutions de persistance, forçant parfois à modifier la logique du code
de l’application, un nouveau modèle de structure d’application a été mis au point : le MVC (Model
View Controller) qui permet de séparer l’exploitation du stockage des données. Cette technique sera
abordée dans le TP final en Java.
8 2950 TG PA 00
3. Sérialisation et transfert d’objets en C/S
Problème
Il est parfois nécessaire de transférer des objets d’un ordinateur à un autre via un réseau,
dans le cadre d’une application client/serveur. Par exemple, pour le jeu de combat en 2D,
plusieurs joueurs peuvent s’affronter via le réseau. Chaque joueur doit savoir quels sont
les autres joueurs qui sont dans l’arène de combat. Donc les objets des différents joueurs
doivent être envoyés à tous les joueurs pour que tout le monde se voit.
Solutions
Dans les techniques de persistance des objets, la sérialisation a été présentée comme
la solution la plus naturelle, la plus directe. La sérialisation ne sert pas qu’à mémoriser
des objets dans un fichier pour une utilisation ultérieure. Elle permet aussi de transfor-
mer l’objet en un format binaire transportable via le réseau. En effet, seuls le texte et
le binaire peuvent transiter sur le réseau. Le fait de transformer l’objet en un format
binaire ouvre alors la possibilité de transférer l’objet dans son intégralité en une ligne
de code, et de même, de le récupérer dans son intégralité à la réception.
L’objet monJoueur, qui se trouve dans l’application sur l’ordinateur du joueur Ed, est
d’abord sérialisé (transformé au format binaire), puis transporté via le réseau vers l’ordi-
nateur du joueur Xzof. L’application du joueur Xzof va récupérer l’objet sérialisé et va le
désérialiser pour mettre son contenu dans l’objet local qui a été nommé sonJoueur. Ainsi,
le joueur Xzof est en possession de toutes les informations de l’objet qui a été créé sur
l’ordinateur du joueur Ed.
8 2950 TG PA 00
Cependant, la sérialisation ne suit pas cette règle : elle réalise une vraie copie de valeur
de l’objet afin de le transférer d’un ordinateur à un autre. Pourquoi le transfert par réfé-
rence ne marche pas dans ce cas ? Tout simplement parce que l’on n’est plus sur le même
ordinateur, donc les références en mémoire ne sont plus les mêmes.
Sérialisation et algorithmique
Il n’existe pas de syntaxe algorithmique spécifique à la sérialisation. Le sujet peut propo-
ser une classe qui gère, à travers des méthodes, la sérialisation et désérialisation d’objets.
Il suffit de respecter la syntaxe donnée dans le sujet, à travers l’interface de la classe
proposée.
Normalement une sérialisation se fait à partir du format standard Object. Lors de la
désérialisation, il reste à transtyper l’objet dans le type attendu.
4. Exercice récapitulatif
À travers cet exercice, vous allez manipuler des curseurs pour accéder à une base de
données relationnelle. Vous avez déjà été confronté à ce type de sauvegarde des objets
dans certains exercices précédents. Le sujet touche aussi au format XML. Voici donc ce
sujet officiel sorti en 2007.
Exercice 1
Séquence 4
Mise à jour des rendez-vous (extrait du cas EDF sujet 2007)
Persistance
des objets Remarque : Les questions de ce dossier peuvent être traitées de manière indépen-
dante.
Page 80
Une base de données permet d’exploiter localement les informations concernant les
rendez-vous. Elle comporte entre autres une table RDV mémorisant l’ensemble des
rendez-vous pris. Cette table doit être mise à jour à partir des informations gérées
par le centre informatique de Mulhouse.
On envisage d’opérer la mise à jour de la table RDV de la manière suivante :
Le centre informatique de Mulhouse génère un fichier XML contenant les ajouts,
modifications et suppressions de rendez-vous à prendre en compte.
Ce fichier est transmis chaque soir au centre de Douvres.
Un programme exploite ce fichier pour mettre à jour la table RDV de la base locale.
La classe GèreRDV (décrite en annexe 1) est dédiée à la réalisation de cette applica-
tion. Elle est destinée à simplifier les opérations de mise à jour de la table RDV dans
la base de données locale.
Le second paramètre des méthodes ajouter et modifier est un objet de la classe
Champs décrite en annexe 1. Dans la méthode ajouter, cet objet contient l’ensemble
des champs à l’exception du numéro de RDV (ce numéro est le premier paramètre).
Dans la méthode modifier, cet objet contient uniquement les champs dont la valeur
doit être modifiée dans la table.
La méthode valeurFormatée(nomChamp, valeurChamp) retourne la valeur correcte-
ment formatée en fonction du type du champ : valeurChamp pour les champs numé-
riques, valeurChamp encadrée par des quotes (apostrophes) pour tous les champs
non numériques.
8 2950 TG PA 00
La méthode valeurFormatée(nomChamp, valeurChamp) retourne la valeur correcte-
ment formatée en fonction du type du champ : valeurChamp pour les champs numé-
riques, valeurChamp encadrée par des quotes (apostrophes) pour tous les champs
non numériques.
valeurFormatée("chargeRdv","45") retourne la chaîne 45 car chargeRdv est un
entier.
valeurFormatée("nomClient","Dubois") retourne la chaîne ‘Dubois’ car nomClient
est une chaîne.
La méthode getType retourne un caractère indiquant le type du champ dont le nom
est passé en paramètre : C pour chaîne, N pour numérique ou D pour date.
Travail à faire
1. Écrire la méthode getNbChamps de la classe Champs.
2. Écrire la méthode valeurFormatée de la classe GèreRDV.
3. Écrire la méthode ajouter de la classe GèreRDV.
L’exploitation du fichier XML doit être réalisée par le programme majTableRDV. Ce
programme utilise les classes NoeudXml et DocXml décrites en annexe 2 pour par-
courir le contenu du document XML et la classe GèreRDV décrite en annexe 1 pour
réaliser les modifications dans la table RDV (ajouts, mises à jour, suppressions).
Le fichier XML à exploiter se nomme modifsRdv.xml. Un exemple de ce fichier XML
et le début du programme majTableRdv sont fournis en annexe 3.
Séquence 4
Travail à faire Persistance
4. Compléter sur la copie, le programme majTableRDV. des objets
Page 81
Annexe 1 Classe Champs, structure de la table RDV et classe GèreRDV
Classe Champs
La classe Champs est destinée à la gestion d’un ensemble de champs d’une ligne de
table relationnelle (nom, valeur). Elle fonctionne globalement comme une collection
de champs.
Classe Champs
Privé ...
nbChamps : entier
// Nombre de champs mémorisés
Public ...
procédure ajouter(unNom : chaîne, uneValeur : chaîne)
// Ajoute un champ de nom unNom et de valeur uneValeur
fonction getNbChamps() : entier
// Renvoie le nombre de champs mémorisés.
fonction getNom(unIndex : entier) : chaîne
// Retourne le nom du champ mémorisé à l’index unIndex.
// Le premier champ est à l’index 0.
fonction getValeur(unIndex : entier) : chaîne
//Retourne la valeur du champ mémorisé à l’index unIndex.
// Le premier champ est à l’index 0.
procédure vider()
// Enlève l’ensemble des champs mémorisés.
Fin Classe
8 2950 TG PA 00
Exemple d’utilisation :
lesChamps : Champs
lesChamps Å new Champs()
lesChamps.ajouter("ref","P01")
lesChamps.ajouter("désignation","souris")
lesChamps.ajouter("prix","12.5")
afficher (lesChamps.getNbChamps()) // affiche 3
nomDuChamp, valeurDuChamp : chaîne
nomDuChamp Å lesChamps.getNom(1)
valeurDuChamp Å lesChamps.getValeur(1)
afficher (nomDuChamp, " : ", valeurDuChamp) // affiche désignation : souris
Structure de la table RDV
numRdv : entier
nomClient : chaîne
adresseClient : chaîne
telClient : chaîne
codeZei : chaîne
numeroContrat : entier
dateRdv : date
chargeRdv : entier
plageHoraire : chaîne> Classe GèreRDV
Classe GèreRDV
Séquence 4
Cette classe permet de mettre à jour la base de données du centre de Douvres. Elle
Persistance opère les ajouts, modifications et suppressions sur la table RDV.
des objets
Classe GèreRDV
Page 82 Privé ...
fonction getType(nomChamp : chaîne) : caractère
// Retourne un caractère indiquant le type du champ (C,N ou D).
fonction valeurFormatée(nomChamp:chaîne, valeurChamp:chaîne) : chaîne
// Retourne la valeur correctement formatée en fonction du type de
// champ.
procédure execSql(sql : chaîne)
// Exécute l’instruction SQL insert, update ou delete passée en
// paramètre.
Public
gèreRDV(chaineConnexion : chaîne)
// constructeur, permet entre autres de se connecter au SGDB en
// utilisant la chaîne de connexion passée en paramètre.
procédure supprimer(numéro : chaîne)
// Supprime dans la table RDV le RDV dont le numéro est passé en
// paramètre.
procédure ajouter(numéro : chaîne, lesChamps : Champs)
// Ajoute une ligne dans la table RDV. Le paramètre lesChamps
// regroupe, dans l’ordre, l’ensemble des champs de la table RDV, à
// l’exception du numéro passé dans le 1er paramètre.
procédure modifier(numéro : chaîne, lesChamps : Champs)
// Modifie une ligne dans la table RDV. Le paramètre lesChamps
// contient uniquement les champs qui doivent être modifiés pour le
// rendez-vous dont le numéro est passé dans le premier paramètre.
Fin Classe
8 2950 TG PA 00
Description de la méthode supprimer de la classe GèreRDV :
procédure supprimer(numéro : chaîne)
// Supprime le RDV dont le numéro est passé en paramètre.
Début
requête : chaîne
requête Å "delete from RDV where numRdv="
requête Å requête + valeurFormatee("numRdv",numéro)//+:concaténation
execSql(requête)
fin
Exemple d’utilisation :
gRdv : GèreRDV
gRdv Å new GèreRDV("Provider=interbase;BD=planning")
gRdv.supprimer("1215")
// Supprime le RDV n° 1215 de la base du centre de Douvres.
lesChamps : Champs
lesChamps Å new Champs()
lesChamps.ajouter("dateRdv", "10/04/2007")
lesChamps.ajouter ("chargeRdv", "45")
gRdv.modifier("1230", lesChamps)
// Utilise les informations contenues dans le paramètre lesChamps pour
mettre à jour le RDV n° 1230. Cette instruction modifie donc les champs
dateRdv et chargeRdv.
Séquence 4
Persistance
Annexe 2 Terminologie XML, classe NœudXml et classe DocXml des objets
8 2950 TG PA 00
Classe NoeudXml
Cette classe représente un élément ou un attribut XML.
Classe NoeudXml
Privé
nom, valeur : chaîne // nom et valeur du nœud.
...
Public
fonction getNom() : chaîne // retourne le nom du nœud XML.
fonction getValeur() : chaîne // retourne la valeur du nœud XML.
fonction nbFils() : entier
// Retourne le nombre d’éléments fils du nœud courant s’il s’agit d’un
// élément XML. Retourne -1 s’il s’agit d’un attribut XML.
fonction getFils(unIndex : entier) : NoeudXml
// Retourne l’élément fils d’index unIndex si le nœud courant est un
// élément XML. Le premier élément fils est à l’index 0. Retourne null
// si le nœud courant est un attribut XML.
fonction nbAttributs() : entier
// Retourne le nombre d’attributs XML du nœud courant s’il s’agit d’un
// élément XML. Retourne -1 s’il s’agit d’un attribut XML.
fonction getAttribut(unIndex : entier) : NoeudXml
// Retourne l’attribut XML d’index unIndex si le nœud courant est un
// élément XML. Le premier attribut est à l’index 0. Retourne null si
Séquence 4 // le nœud courant est un attribut XML.
...
Persistance
des objets Fin Classe
8 2950 TG PA 00
Le tableau ci-dessous montre la trace de l’exécution d’une série d’instructions.
Instruction pdt.nom pdt.valeur att.nom att.valeur nd.nom nd.valeur
rac,pdt,att,n : NœudXml
doc : DocXml
...
doc Å new DocXml()
doc.charger("pdts.xml")
rac Å doc Racine() produit
pdt Å rac.getFils(0) produit ref P01
att Å pdt.getAttribut(0) produit ref P01
nt Å pdt.getFils(0) produit ref P01 désignation souris
nd Å pdt.getFils(1) produit ref P01 prix 12.5
Fichier modifsRdv.xml
<infosRdv>
<rdv action="ajout">
<numRdv>1245</numRdv>
<nomClient>Dubois</nomClient>
<adresseClient>12 rue des fleurs 14000 Caen</adresseClient> Séquence 4
<telClient>0429784529</telClient>
<codeZei>CA</codeZei> Persistance
des objets
<numeroContrat>287</numeroContrat>
<dateRdv>12/04/2007</dateRdv>
Page 85
<chargeRdv>50</chargeRdv>
<plageHoraire>mat</plageHoraire>
</rdv>
<rdv action="modif">
<numRdv>1230</numRdv>
<dateRdv>10/04/2007</dateRdv>
<chargeRdv>45</chargeRdv>
<plageHoraire>apm</plageHoraire>
</rdv>
<rdv action="supp">
<numRdv>1215</numRdv>
</rdv>
</infosRdv>
8 2950 TG PA 00
Remarques :
Dans un élément <rdv action="ajout"> ou <rdv action="modif">, les éléments fils
apparaissent toujours dans le même ordre que les champs de la table RDV.
L’élément fils numRdv est présent dans tous les éléments <rdv>, quelle que soit
l’action.
Le contenu du fichier modifsRdv.xml est automatiquement contrôlé lors de sa créa-
tion et peut donc être considéré comme fiable : il contient toujours au moins une
action et chaque action, selon son type, comporte au moins la valeur d’un champ.
Programme majTableRdv
Programme majTableRdv
début
doc: DocXml
doc Å new DocXml ()
gRdv : GèreRDV
gRdv Å new GèreRDV("Provider=interbase;BD=planning")
racine : NoeudXml
doc.charger("modifsRdv.xml")
racine Å doc.racine()
...
fin
Séquence 4
Persistance
des objets
Page 86 Synthèse
BDR : enregistrer les propriétés dans une base de données relationnelle et y accéder
par un curseur
Sérialisation : transformer l’objet en un codage au format texte pour le mémoriser
dans un fichier ou le transférer via un réseau (la désérialisation réalise l’opération
inverse).
XML : enregistrer les propriétés dans un fichier XML.
BDO : travailler directement avec une base de données objet qui contient les classes
de l’application et les instances créées.
8 2950 TG PA 00
Séquence 5
Notions évoluées
Cette séquence présente plusieurs notions plus évoluées dans le domaine
de la programmation objet. Vous allez découvrir des design patterns (mo-
tifs de conception), aller plus loin dans les tests unitaires et apprendre
qu’il existe des outils d’aide au développement objet. Cette séquence ne
comporte pas d’exercices car, soit les notions ne pourront être utilisées
que dans le TP (Refactoring), soit elles sont d’une certaine complexité et
ne sont présentées qu’à titre informatif (Délégation, Factorisation) soit
enfin vous allez directement réaliser une manipulation sous Eclipse (Tests
unitaires) en guise d’exercice guidé.
X Prérequis
Avoir totalement acquis les connaissances abordées dans les séquences précé-
dentes.
Page 87
X Contenu
1. Refactoring...................................................................................................... 88
2. Délégation....................................................................................................... 89
3. Factorisation ................................................................................................... 91
4. Tests unitaires ................................................................................................. 93
Synthèse ........................................................................... 97
8 2950 TG PA 00
1. Refactoring
La notion de refactoring (refactorisation) représente le processus qui consiste à aider le
développeur à modifier ou améliorer le code sans pour autant avoir une incidence sur
l’exécution.
La plupart des environnements offrent un ensemble d’outils de refactorisation. Vous
allez travailler dans le TP avec l’environnement Eclipse pour Java (version Ganimède).
Comme Visual Studio que vous avez utilisé pour coder en C#, Eclipse permet d’accéder
à ce genre d’outils.
Voici quelques exemples d’améliorations concernées par le refactoring.
Réorganisation du code
Certains environnements offrent des outils pour corriger toutes les indentations dans le
code. C’est bien sûr très pratique pour contrôler si par exemple une accolade n’a pas été
oubliée. Sous Eclipse, si vous faites Ctrl+Shift+F, tout le code se réorganise automatique-
ment. Pensez-y quand vous réaliserez le TP.
Renommage
Il est parfois nécessaire de renommer une classe, une méthode, une propriété, un para-
mètre ou quoi que ce soit d’autre dans le code. Le problème directement lié à ce genre
de modification est la répercution sur tout le reste du code. En effet, si vous renommez
par exemple une méthode, il est important de la renommer partout où elle est utilisée
pour éviter une erreur d’exécution.
Séquence 5
Eclipse, comme d’autres environnements, offre un outil qui permet de renommer n’im-
Notions évoluées porte quoi et de répercuter la modification partout dans le projet. Il est fortement
conseillé d’utiliser cet outil plutôt que de tout renommer car l’outil vous garantit une
Page 88 modification propre dans tout le projet.
Extraction de méthode
C’est encore une fois une facilité offerte par certains environnements. L’extraction de
méthode consiste à faire un bloc autour de plusieurs lignes de code existantes dans le
projet (par exemple dans une méthode) et de créer une nouvelle méthode ne contenant
que ce code. L’extraction s’occupe aussi de supprimer le code du bloc d’origine pour le
remplacer par l’appel de la nouvelle méthode. Ce petit outil peut s’avérer bien pratique
dès que vous réalisez qu’il y a des répétitions de code dans votre projet.
Encapsulation de propriété
La demande d’encapsulation automatique d’une propriété (qui est donc actuellement
publique ou non typée) va placer cette propriété en privé, générer le getter et setter
nécessaire et modifier dans le projet l’accès à cette propriété en passant par le getter ou
setter suivant les besoins.
Extraction d’interface
La sélection de plusieurs membres d’une classe avec la demande d’extraction en inter-
face permet de créer une interface à partir de ces membres et d’implémenter la classe
d’origine avec cette nouvelle interface. Cet outil est utile lorsque des membres sont
identiques (nom et signature) dans plusieurs classes : l’optimisation du code à travers la
création d’une interface va être facilitée.
8 2950 TG PA 00
Suppression de paramètres
Cet outil de refactorisation permet de supprimer un paramètre d’une méthode et de
répercuter cette suppression sur tous les appels de cette méthode.
Réorganisation de paramètres
Le principe est très similaire à l’outil précédent. En demandant la réorganisation des
paramètres d’une méthode, tous les appels à cette méthode sont modifiés automatique-
ment en conséquence.
... et le reste
Suivant les environnements, vous avez accès à différents outils de refactorisation. À vous
de les exploiter suivant vos besoins. Faites en sorte de limiter les modifications de codes
par vous même lorsqu'un outil peut le faire pour vous en toute sécurité. Pensez-y lorsque
vous coderez le TP.
2. Délégation
La notion de délégation couvre plusieurs domaines. Globalement, elle met en jeu tou-
jours cette volonté de déléguer le travail, donc de faire en sorte que le travail soit réalisé
ailleurs. Certains associent la notion de délégation avec la notion de pointeur sur fonc-
tion : c’est un autre moyen pour exécuter une fonction.
Voici quelques cas où le terme "délégation" est utilisé. Séquence 5
// contructeur
public ClassA(InterfB objDelegue) {
this.objDelegue = objDelegue ;
}
// constructeur surchargé
public ClassA() {
this(new ClassB()) ;
}
8 2950 TG PA 00
Avant d’expliquer la délégation, regardez les constructeurs. Cela n’a rien à voir avec la
délégation mais c’est intéressant d’expliquer leur fonctionnement. Si un objet est instan-
cié à partir de ClassA, deux possibilités sont offertes : soit un paramètre est envoyé au
constructeur et ainsi la propriété est valorisée avec le paramètre, soit le constructeur est
appelé sans paramètre et du coup ce constructeur appelle (avec this) le premier construc-
teur en lui envoyant en paramètre une nouvelle instance de ClassB.
Revenons à la notion de délégation. ClassA possède une propriété de type interface
(InterfB) qui est valorisée avec une instance de ClassB (une classe qui implémente cette
interface). Appeler la méthode m1() sur un objet de ClassA revient donc à appeler m1()
sur un objet de ClassB. Le travail est donc délégué à ClassB.
8 2950 TG PA 00
// utilisation en paramètre d’une méthode
public void secondTest(string information, AfficherDelegate affichage){
// si le délégué fait bien référence à une méthode
if (affichage != null) {
// appel des méthodes correspondantes avec le paramètre information
affichage(information) ;
}
}
La déclaration (première ligne) avec le mot réservé "delegate" permet de créer une classe
AfficherDelegate (d’où son utilisation comme un type) mais en forçant les méthodes qui
seront associées à un objet de cette classe à posséder la même signature que lors de la
déclaration du délégué.
Observez bien ensuite le code : remarquez que l’objet déclaré à partir du délégue peut
recevoir des méthodes (en affectant le nom d’une méthode, on affecte en fait l’adresse
de la méthode). Ensuite, l’appel de cet objet provoque l’appel de toutes les méthodes
associées.
3. Factorisation
La factorisation (appelée aussi Fabrique ou Factory en anglais) est un motif de concep-
tion qui permet d’instancier dynamiquement des sous classes.
Séquence 5
Voici tout de suite un exemple pour mieux comprendre (en Java) :
public interface Forme {
Notions évoluées
public double surface() ;
public double perimetre() ;
Page 91
}
8 2950 TG PA 00
Les 2 classes Carre et Cercle implémentent l’interface Forme, donc redéfinissent les 2
méthodes qui la composent. La classe FormeFactory va permettre de générer soit des
objets de type Carre, soit des objets de type Cercle suivant les besoins du programme.
Par exemple, imaginons l’extrait de code suivant :
...
uneForme = FormeFactory.getForme("carre", 15) ;
...
L’objet uneForme va alors recevoir une instance de la classe Carre.
Cet exemple est très simpliste pour comprendre le principe de la Fabrique (qui donc sert
d’intermédiaire pour fabriquer des objets). L’exemple est tellement simpliste que, même
si vous l’avez compris, vous ne devez pas en comprendre l’utilité. Remarquez tout de
même que c’est l’usine FormeFactory qui s’occupe d’instancier les classes Carre ou Cercle.
Ce qui veut dire que si ces classes subissent des modifications en particulier au niveau
des signatures de constructeurs, ça ne change rien à la ligne qui valorise notre objet une
orme. De plus, cette ligne de valorisation est unique en passant par FormeFactory : s’il
n’y avait pas cette classe de Fabrique, on aurait été obligé de faire un test pour savoir si
la forme à créer est un carré ou un cercle et instancier la classe correspondante. Ce test,
il est bien présent mais une seule fois dans la fabrique.
Imaginons cette fois un autre exemple plus complexe. Si vous avez une application qui
a besoin de récupérer des informations provenant de bases de données ou de fichiers
xml, vous pouvez faire en sorte que la construction du curseur d’accès et de lecture de
la base de données ou du fichier xml soit sur le même modèle. Observez le code suivant,
Séquence 5 qui a été volontairement très simplifié mais qui contient le squelette nécessaire pour
comprendre le principe (principe similaire à l’exemple précédent) :
Notions évoluées
public interface Curseur {
public Object read() ;
Page 92
...
}
8 2950 TG PA 00
public class CurseurFactory {
public static Curseur getCurseur(String fichier) {
// teste si le fichier est au format XML
if (...) {
return new XmlReader(fichier) :
}else{
// teste si le fichier est au format base de données
if (...) {
return new DataBaseCurseur(fichier) ;
}else{
// aucun format n’est reconnu pour ce fichier
return null ;
}
}
}
}
On peut ensuite imaginer la création suivante :
...
unCurseur = CurseurFactory.getCurseur("monfichier.xml") ;
...
L’objet unCurseur sera une instance de la classe XmlCurseur et donc permettra de faire
toutes les opérations nécessaires, entre autres la lecture, sur le fichier passé en para-
mètre.
Séquence 5
Les tests unitaires sont nés suite à un constat : il n’est pas toujours facile de contrôler Page 93
qu’un programme marche correctement et il n’est pas rare de tomber un jour ou l’autre
sur une erreur.
Pour éviter se désagrément, 2 solutions : soit il faut faire une batterie de tests « à la
main » sur chaque partie de programme pour contrôler son efficacité, soit on utilise un
framework de tests unitaires (comme JUnit pour java).
Exemple d’utilisation
Pour voir concrètement comment marchent les tests unitaires, vous allez tester vous-
même un exemple directement en Java sous Eclipe, environnement que vous allez
utiliser dans le TP. Donc avant de passer à la suite, faites le premier point du TP (1. Les
outils nécessaires) qui se trouve dans le fascicule TP de ce module. Vous allez y trouver
les explications nécessaires pour installer et configurer les outils pour coder en java sous
Eclipse). En fin de fascicule TP, vous trouverez un mini mémento Java.
8 2950 TG PA 00
Une fois Eclipse lancé, si un dossier de travail vous est demandé, donnez le chemin d’un
dossier que vous aurez préalablement créé sur votre disque et qui contiendra vos tests
de code. Si vous tombez sur l’écran de bienvenue (comme expliqué dans le TP), cliquez
sur le rond contenant une flèche (qui mentionne, si vous passez la souris dessus, "Plan
de travail").
Dans le menu "Fichier", choisissez "Nouveau", "Projet...". Dans la fenêtre qui s’ouvre,
dossier "Java", sélectionnez "Projet Java" puis cliquez sur "Suivant". Dans la nouvelle
fenêtre, tapez le nom du projet "TestUnitaire" et cliquez sur "Terminer".
Le nouveau projet se crée. Dans la fenêtre de gauche, partie "Explorateur", faites un clic
droit sur "TestUnitaire" et choisissez "Nouveau", puis "Classe". Dans la nouvelle fenêtre,
au niveau du Nom (et pas du Package), tapez "CalculMath" puis "Terminer".
La classe a été créée. Dans cette classe, pour faire ce test, on va juste créer une méthode
qui permet de calculer la factorielle d’un nombre (rappel : la factorielle d’un nombre est
la multiplication de tous les entiers de ce nombre à 1). Donc placez dans la classe le code
de la méthode fac :
public class CalculMath {
8 2950 TG PA 00
Remarquez la méthode testFac qui est remplie avec un fail, en attendant d’y insérer un
code de test. Pour réaliser ces tests, on va utiliser des méthodes assert dont la logique
de fonctionnement a été un peu vue dans le cours « Développement d’applications ».
Remplissez la classe avec les informations suivantes :
import junit.framework.Assert;
import junit.framework.TestCase;
}
La méthode statique assertEquals permet de comparer 2 valeurs et de retourner un mes-
sage spécifique si les 2 valeurs ne sont pas égales. Vous vous doutez qu’il existe d’autres
méthodes statiques assert qui permettent de faire des tas d’autres choses.
Il est temps de lancer le test. Dans le menu "Lancer", partie "Exécuter en tant que",
sélectionnez "Test JUnit". Le test est lancé. Vous devriez obtenir, à gauche, une barre
verte indiquant que tout est ok. Donc on sait que la méthode marche pour 5. Dans les
Séquence 5
tests unitaires, il faut tenter de penser à tous les cas particuliers. Pour le calcul de la fac-
torielle, le cas particulier, c’est 0 puisque factorielle de 0 doit donner par convention 1. Notions évoluées
Pour bien distinguer les 2 types de tests, faisons 2 méthodes avec des noms explicites.
Modifiez le contenu de la classe pour obtenir ceci : Page 95
import junit.framework.Assert;
import junit.framework.TestCase;
}
Pour relancer le test, il suffit de cliquer sur la petite flèche verte qui est au dessus de
la barre verte et qui s’appelle "Réexécuter le test". Cette fois la barre est rouge pour
annoncer un échec. Si vous regardez en dessous de la barre, vous trouvez la liste des
méthodes du test avec soit une coche verte pour signaler que le test est ok, soit une
8 2950 TG PA 00
croix pour signaler le problème. Cela permet déjà de savoir précisément quelle méthode
échoue. Mais il y a mieux : en bas à gauche, dans la partie "Trace de l’échec", placez la
souris sur la première ligne pour la lire complètement et vous verrez qu’Eclipse vous dit
clairement le problème : "Expected <1> but was <0>". Donc le test attendait la valeur 1
mais la méthode fac lui a retourné la valeur 0.
Effectivement, dans le cas où le paramètre contient 0, on ne retourne pas la bonne
valeur. Il faut corriger le code de la classe. Soit on ajoute un test, soit on change l’initia-
lisation et la borne supérieure du for. Voici la version corrigée :
public class CalculMath {
}
Après cette correction, relancez le test. Cette fois il est vert.
Petit résumé
Pour réaliser des tests unitaires, il faut utiliser une classe appropriée (par exemple JUnit
Séquence 5 pour le langage Java). Les tests peuvent être écrits à la main mais la plupart des environ-
nements offrent des outils qui génèrent automatiquement les tests.
Notions évoluées
La classe du test unitaire doit porter le même nom que la classe à tester, suivi de "Test".
Page 96 Elle doit hériter de TestCase.
Par convention, les noms des méthodes du test unitaire sont identiques aux méthodes
à tester, avec comme préfixe "test". Cependant on peut modifier ces noms suivant les
besoins.
L’environnement va permettre d’exécuter le test et contrôler graphiquement le résultat.
Cela n’a pas été présenté ici, mais il est aussi possible d’exécuter un test unitaire en ligne
de commande.
8 2950 TG PA 00
Synthèse
Séquence 5
Notions évoluées
Page 97
8 2950 TG PA 00
Séquence 6
UML, la modélisation objet
Cette séquence aborde UML qui devient de plus en plus incontournable,
en particulier pour la modélisation de classes.
X Prérequis
Avoir une très bonne connaissance de la programmation objet, donc de préfé-
rence avoir acquis les connaissances abordées dans les séquences précédentes.
X Contenu
1. Introduction .................................................................................................. 100
2. Cas d’utilisation ............................................................................................ 101
Séquence 6
3. Diagramme de classes .................................................................................. 107
4. Diagramme d’états ....................................................................................... 117 UML, la
modélisation objet
5. Diagramme de séquences ............................................................................ 120
6. Les autres diagrammes ................................................................................ 122 Page 99
8 2950 TG PA 00
1. Introduction
UML (Unified Modeling Language ou « langage de modélisation unifié ») est un langage
de modélisation graphique à base de pictogrammes. Il s’impose depuis plusieurs années
comme un standard dans le domaine de la conception orientée objet.
Vous avez eu l’occasion de manipuler, dans différents exercices, des classes qui sont
souvent liées entre elles. UML permet de visualiser graphiquement les classes et les liens
entre les classes : cette visualisation est une aide considérable pour optimiser l’organisa-
tion des informations.
À partir de cette première remarque, vous devez penser qu’UML permet de modéliser
des classes et rien d’autre. C’est bien sûr totalement faux. En réalité, de nombreux dia-
grammes permettent d’avoir une représentation visuelle de toutes les étapes du proces-
sus de développement.
Vous avez commencé à découvrir le modèle conceptuel de données de la méthode
Merise pour la modélisation des bases de données relationnelles. UML est souvent consi-
déré comme le successeur nettement plus puissant que Merise. En réalité, les choses sont
plus nuancées. UML est nettement plus orienté objet cependant il est vrai qu’un schéma
conceptuel de données (de Merise) peut facilement se traduire en diagramme de classes
(d’UML), alors que le contraire n’est pas gérable.
Compte tenu de la popularité des 2 méthodes, il est préférable d’avoir des connaissances
de base aussi bien dans l’une que dans l’autre.
Problème d’informatisation
MERISE UML
Qu’est-ce qu’UML ?
UML existe depuis 1997 et représente l’unification de plusieurs méthodes objet déjà
existantes (OMT, Booch et OOSE). Il s’impose maintenant comme le standard dans le
domaine de la modélisation objet.
8 2950 TG PA 00
Les 13 diagrammes
Comme Merise, UML propose plusieurs modèles, sous formes de diagrammes. Nous
allons faire le tour de l’ensemble des diagrammes, en s’arrêtant plus particulièrement
sur le diagramme de classes.
Pourquoi insister sur le diagramme de classes ? Parce que c’est celui qui va le plus vous
servir puisqu’il permet de modéliser les classes nécessaires d’une application. Il est en
quelque sorte l’équivalent du MCD (Modèle Conceptuel de Données) de Merise. Il peut
d’ailleurs aussi se substituer au MCD.
Cependant, il est important que vous ayez une vision globale des autres diagrammes,
certains étant très connus.
Certains n’utilisent plus la méthode Merise mais directement UML pour tout modéliser.
Au niveau du référentiel aucune méthode spécifique n’est imposée mais il est tout de
même spécifié que pour les données, le modèle conceptuel de la méthode Merise reste
incontournable, et pour les classes, c’est le diagramme de classes d’UML qui est préco-
nisé. Cela suppose qu’à l’examen, aucune méthode ne peut vous être imposée mais vous
devez avoir les connaissances nécessaires pour tout modéliser, quelle que soit la méthode
choisie. Vous devez aussi avoir une connaissance assez large pour être capable de lire un
schéma existant, quelle que soit la méthode.
Voici donc les 13 diagrammes d’UML 2 (UML 1.3 en comptait 9) dont certains sont remis
dans le contexte du jeu abordé dans le cours objet pour que vous compreniez mieux,
à travers cet exemple, l’intérêt de chaque diagramme. Attention, les exemples de dia-
grammes présentés n’ont pas la prétention d’être exhaustifs : le but est juste de vous
Séquence 6
donner une idée générale par rapport au contexte choisi.
UML, la
modélisation objet
Exemple
Un joueur se connecte au serveur. Une fois connecté, il choisit un personnage après avoir
éventuellement consulté le catalogue des personnages. Il saisit son pseudo. Il peut alors
entrer dans l’arène et jouer (se déplacer, attaquer les adversaires avec des boules). Il peut
aussi, pendant le jeu, dialoguer avec les autres joueurs (chat). À tout moment (connecté
ou non), le joueur peut consulter l’aide du jeu.
8 2950 TG PA 00
Séquence 6
Analyse du schéma
UML, la
modélisation objet Il faut se positionner sur l’acteur (joueur) qui va pouvoir interagir avec le système. Que
peut-il faire ?
Page 102 • Il peut se connecter.
• Ce n’est qu’une fois connecté qu’il peut choisir un personnage (d’où l’include qui
marque la dépendance obligatoire d’une action par rapport à une autre)
• Le choix du personnage peut se faire optionnellement après avoir consulté le cata-
logue (d’où l’extend qui marque la dépendance optionnelle d’une action par rapport
à une autre).
• L’entrée dans l’arène ne peut se faire qu’après le choix du personnage (d’où l’include).
• Une fois dans l’arène, il peut dialoguer ou jouer (ces 2 actions sont indépendantes
l’une de l’autre, mais dépendent toutes les 2 de l’entrée dans l’arène).
• Quand il joue, il peut soit se déplacer, soit attaquer (ces 2 actions sont des « sous
actions » de l’action jouer).
• Enfin, indépendamment de tout, le joueur peut à tout moment consulter l’aide.
8 2950 TG PA 00
Syntaxe
Le diagramme de cas d’utilisation respecte les règles de représentation suivantes :
Acteur externe
Il interagit avec le système
Acteur interne
Il fait partie du système
Cas d’utilisation
Action possible réalisée par un acteur
Association de communication
Lien entre l’acteur et l’action possible
Inclusion obligatoire
Une action découle obligatoirement d’une autre action
Inclusion optionnelle
Une action découle optionnellement d’une autre action
En complément du schéma, chaque cas d’utilisation peut ensuite être détaillé de façon
textuelle dans un tableau. Voici un exemple détaillant le cas d’utilisation « se connec-
ter » :
Intérêts Accéder au serveur afin d’être authentifié et pouvoir accéder au jeu Page 103
Exercice 1
Vous devez créer un site commercial. Sur le site, un utilisateur peut sélectionner
des produits éventuellement après avoir lu la fiche technique de chaque produit. Il
peut ensuite visualiser son panier. S’il décide de valider son panier, il doit donner
ses coordonnées et il peut alors soit choisir de payer en ligne, soit de payer à la
réception.
Dessiner le diagramme de cas d’utilisation correspondant.
8 2950 TG PA 00
Exercice 2
Extrait du Cas VALIERS (métropole 2003)
Le déroulement d’une épreuve :
Le jury contrôle le déroulement de chaque épreuve et établit le classement à partir
du barème correspondant (voir annexe 1.F).
Illustration pour la première phase de l’épreuve n° 4, au barème AC (A avec chrono) :
Lucky Luke s’est engagé dans l’épreuve n° 4 du Concours du Printemps de juin 2002
avec Jolly Jumper.
Lors de son passage dans la première phase de l’épreuve n° 4, le couple Lucky Luke-
Jolly Jumper n’a commis qu’une faute (un refus sur l’obstacle 7). Il effectue le par-
cours dans un temps de 59,05 secondes.
L’annexe 3 propose un extrait du scénario qui explique l’interaction homme-machine
(IHM) correspondant à l’enregistrement des résultats d’une phase (annexe 3.A) et
une maquette d’interface renseignée à partir de l’exemple précédent (annexe 3.B).
Travail à faire
Proposer le scénario correspondant à l’enregistrement des résultats d’une épreuve
ne comportant qu’une phase au barème C.
UML, la
modélisation objet
Page 104
8 2950 TG PA 00
1.B Obstacles retenus pour le barrage de l’épreuve n °4
Séquence 6
UML, la
modélisation objet
Page 105
1.D Exemple de programme pour un concours
8 2950 TG PA 00
1.E Les catégories en CSO
Séquence 6
UML, la
modélisation objet
Page 106
Annexe 3 Gestion des épreuves
3.A Éléments de scénario pour l’enregistrement des résultats d’une phase au barème AC
8 2950 TG PA 00
3. Diagramme de classes
Le diagramme de classes permet de modéliser données et traitements de l’application.
Question : « Quelles sont les informations et traitements nécessaires ? »
Un diagramme de classes, c’est :
• un schéma représentant les liens entre les classes ;
• l’ensemble des données et traitements nécessaires ;
• une organisation détaillée de contenu de l’application.
Exemple
" Le but du jeu est de permettre à plusieurs joueurs de se connecter à un serveur et de
jouer ensemble. Chaque joueur choisit un personnage, donne un pseudo et rentre dans
l’arène. L’arène contient des murs. Le joueur peut se déplacer entre les murs, il peut tirer
des boules pour toucher les adversaires. Chaque joueur touché perd de la vie. Chaque
joueur qui touche un autre gagne de la vie. En fin de vie, le joueur meurt. "
Séquence 6
UML, la
modélisation objet
Page 107
Dans un esprit de simplification, seuls les liens entre les classes apparaissent. En réalité, il
faudrait aussi détailler le contenu des classes (propriétés et méthodes).
Analyse du schéma
Un Jeu peut être soit JeuClient, soit JeuServeur. Tout est géré côté serveur (le client se
contente d’afficher et envoyer les commandes utilisées). Le JeuServeur génère des murs
et aussi des joueurs (à chaque connexion). Chaque joueur possède un Label et une Boule
qu’il peut lancer quand il veut (d’où le thread Attaque qui permet de lancer une boule
8 2950 TG PA 00
dans un thread indépendant, pendant que le joueur peut continuer à jouer). Les murs,
les boules et les joueurs sont des objets (de la classe Objet). Cette classe va donc contenir
des propriétés et méthodes communes à tous les objets.
Syntaxe
Le diagramme de classes respecte les règles de représentation suivantes :
Classe
Sa représentation graphique est découpée en 3 parties :
- partie haute : contient le nom de la classe, et éventuellement une précision pour les
classes spécifiques (<<thread>>, <<interface>>...). Le nom est en italique pour les classes
abstraites
- partie centrale : contient les propriétés
- partie basse : contient les méthodes
La classe, ses propriétés et ses méthodes sont précédées d’un signe pour préciser la portée :
- : privé + : public # : protégé
Spécialisation
Héritage entre les classes
Association
1..* 1..* Elle se traduira, suivant les cardinalités, par une propriété dans une classe (ou plus
souvent les 2) contenant une instance ou une collection d’instances de l’autre classe.
UML, la
Association porteuse
modélisation objet
1..* 1..* Elle représente une classe qui contient des informations nécessitant 2 classes pour y
accéder. Elle se traduit généralement par un dictionnaire dans l’une ou l’autre classe
Page 108 (ou les 2) portant en clé une instance de l’autre classe, et en contenu les propriétés
de l’association porteuse (vous allez peut-être mieux comprendre, plus loin, dans un
exemple vu à travers un exercice)
Agrégation
Elle traduit une notion de constituant/constitué. Cependant le constituant a une vie
propre et peut servir ailleurs. L’agrégation est une association particulière : elle porte
donc aussi des cardinalités
Composition
Elle traduit une notion forte de dépendance de type composant/composé. Si une
instance du côté du losange est supprimée, alors toutes les instances dépendantes de
l’autre classe sont aussi supprimées car elles n’ont pas de vie seule.
Exemple : JeuServeur - Joueur (les joueurs ne peuvent exister si JeuServeur disparait).
Joueur - Boule (la boule du joueur ne peut exister sans son joueur)
Multiplicité (cardinalité)
1 Elles sont sur les branches des associations.
* (ou) 0..* À une instance correspond une seule autre
1..* À une instance correspond plusieurs autres
À une instance correspond plusieurs autres, minimum une
8 2950 TG PA 00
Remarque importante sur les multiplicités :
Attention, les multiplicités fonctionnent à l’envers par rapport aux cardinalités de Merise.
Lisons par exemple les multiplicités sur le lien entre JeuServeur et Joueur :
• un JeuServeur peut avoir plusieurs joueurs (le * est du coté de Joueur !) ;
• un Joueur ne peut être rattaché qu’à un JeuServeur (le 1 est du côté de JeuServeur !)
Évitez de tout mélanger entre les 2 méthodes...
Exercice 3
8 2950 TG PA 00
Annexe 1 Diagramme de classe partiel à compléter
Atelier
lesVéhicules 1..n
lesTypesExistants
1..n Véhicule
- numImma : chaîne
EntretienType - nbKmActuel : entier
- code : entier
- nbKm : entier + getNbKmActuel() : entier
- nbKmToléré : entier + getUnEntretien(index : entier) : Entretien
+ getNbEntretiens() : entier
+ getNbKm() : entier + ajouteEntretien(uneDate : date, unCommentaire : chaîne, unType : EntretienType)
+ getNbKmToléré() : entier
La classe EntretienType permet de recenser tous les types d’entretiens et ceci quels que
soient les véhicules.
UML, la
modélisation objet
Page 110
8 2950 TG PA 00
Remarque : Tous les arguments des méthodes sont en "entrée".
Séquence 6
UML, la
modélisation objet
Page 111
8 2950 TG PA 00
Exercice 4
UML, la
modélisation objet Exemple d’utilisation :
// persist est une instance de PersistanceSQL
Page 112 Distributeur leDistributeur Å persist.chargerDepuisBase ("2",
"Distributeur")
// leDistributeur est l’instance de la classe Distributeur dont
l’identifiant est 2.
Toutes les commandes du distributeur sont automatiquement chargées dans la collec-
tion leDistributeur.lesCommandes. Chaque produit commandé est également chargé,
et se trouve donc référencé par le champ leProduit de l’objet Commande correspon-
dante.
Classe Distributeur
Privé
id : chaîne
nom : chaîne
lesCommandes : Collection de <Commande>
// Toutes les commandes du distributeur.
Public
// Constructeur
Distributeur (unId : chaîne , unNom : chaîne)
// Construit un objet Distributeur. À ce stade, il n’est pas stocké
// dans la base de données.
8 2950 TG PA 00
fonction GetId () : chaîne
// Retourne l’identifiant du distributeur.
Classe Commande
Privé
id : entier // Identifiant de la commande.
leProduit : Produit // Le produit commandé (catégorie de noix).
prixHt : réel // Prix unitaire du produit négocié avec le client.
conditionnement : chaîne // Type de conditionnement.
quantité : entier // Quantité de produits conditionnés
commandée.
dateConditionnement : Date// Date de conditionnement de la commande.
dateEnvoi : Date // Date d’envoi de la commande.
Public Séquence 6
// Constructeur
UML, la
Commande (...) modélisation objet
// Construit un objet Commande, la liste des paramètres est sans
// importance. Le champ dateEnvoi est initialisé à NULL. Page 113
fonction GetId () : entier
// Retourne l’identifiant de la commande.
fonction GetProduit () : Produit
// Retourne le produit commandé.
fonction GetPrixHt () : réel
// Retourne le prix unitaire négocié avec le client.
fonction GetConditionnement () : chaîne
// Retourne le type de conditionnement des produits de cette commande.
fonction GetQuantité () : entier
// Retourne la quantité commandée.
fonction EnCours () : booléen
// Renvoie vrai si la commande n’est pas encore expédiée, faux sinon.
// Une commande n’est pas expédiée si sa date d’envoi contient NULL.
fonction XmlCommande () : chaîne
// Retourne la chaîne correspondant au code XML représentant la
// commande Cette fonction est appelée par la méthode XmlNonLivrées()
// de la classe GestionCommandes décrite ci-après.
Fin classe
8 2950 TG PA 00
Classe Produit
Privé
variété : chaîne // Variété de noix, exemple : ¨Mayette¨.
type : chaîne // Type de noix, exemple : ¨fraîche entière¨.
calibre : entier // Calibre des noix, exemple : 2.
Public
// Constructeur
Produit (...)
// Construit un objet Produit, la liste des paramètres est sans
// importance.
Fin classe
Séquence 6
Classe GestionCommandes
UML, la Privé
modélisation objet données : PersistanceSQL
// Attribut qui permet de rendre les objets métiers accessibles.
Page 114
Public
//Constructeur
GestionCommandes (lesDonnees : PersistanceSQL)
// Construit un objet GestionCommandes avec un modèle de persistance
// associé.
8 2950 TG PA 00
{
xml : chaîne
xml Å '<?xml version="1.0" encoding="UTF-8"?>'
xml Å xml+'<commandes idDistributeur="'+unDistributeur.GetId()+'"'
xml Å xml+'xmlns:xlink="http://www.w3.org/1999/xlink">'
enCours : Collection de <Commande>
enCours Å unDistributeur.GetCommandesEnCours()
laCommande : Commande
Pour chaque laCommande dans enCours faire
xml Å xml+laCommande.XmlCommande()
FinPour
xml Å xml+"</commandes>"
retourner xml
}
Fin classe
Exercice 5
8 2950 TG PA 00
Classe Secteur
Privé
numSecteur : Entier
nomSecteur : Chaîne // nom du quartier
espaceVert : Booléen
// Indique la présence ou non dans le quartier d’un espace vert
// municipal à arroser
laCommune : Commune
lesBranchements : Collection de Branchement
Public
Secteur(unNuméroSecteur : Entier, unNomSecteur : Chaîne,
unEspaceVert : Booléen, uneCommune : Commune)
fonction getNumSecteur() : Entier
fonction getNomSecteur() : Chaîne
fonction getEspaceVert() : Booléen
...
Fin Classe Secteur
Classe Branchement
Privé
leCompteur : Compteur
Public
Séquence 6
Fonction conso() : Entier
UML, la Début
modélisation objet retourner leCompteur.relevé()
Fin
Page 116 ...
Fin Classe Branchement
Classe Compteur
Privé
indexAncien : Entier
indexNouveau : Entier
Public
Fonction relevé() : Entier
Début
retourner (indexNouveau – indexAncien)
Fin
...
Fin Classe Compteur
8 2950 TG PA 00
Exercice 6
+ Catégorie
- code
- libellé
quantité Jouet 1 + GetCode
- numéro + GetLibellé
1..* catég + AjouterJouet
- libellé
Catalogue
+ GetNuméro lesJouets
- année 1..* 1..*+ GetLibellé
+ GetAnnée + GetCatég
+ QuantitéDistribuée lesJouets + GetTranche
1 TrancheAge
+ StatCatég + Convient 1..*
+ GetInfos - code
tranche - ageMin
- ageMax
+ GetCode
+ GetAgeMin
+ GetAgeMax
Séquence 6
Exemple
Une fois le joueur créé, le choix du personnage va permettre de valoriser les propriétés
concernées. Le joueur entre dans l'arène et peut ainsi accéder aux méthodes qui vont
lui permettre de se déplacer et/ou d'attaquer. Le joueur peut aussi être touché ce qui va
diminuer sa vie. Si la vie arrive à 0, le joueur meurt. L'objet est détruit.
8 2950 TG PA 00
se déplacer
Séquence 6
Analyse du schéma
UML, la L'objet est créé à partir de l'état initial. Sa vie passe d'abord par le choix du personnage
modélisation objet (le sens des flèches montre les possibilités de l'évolution de la vie d'un objet). Sans
détailler tout le reste, on remarque que parfois les flèches bouclent sur un même état
Page 118 transitoire. Effectivement, le joueur peut attaquer plusieurs fois. Certaines transitions
(flèches) peuvent aussi être faites sous condition (vie=0). Un objet a forcément une fin
de vie : c'est l'état final.
Syntaxe
Le diagramme d'états respecte les règles de présentation suivantes :
État initial
Début de vie de l'instance.
État final
Fin de vie de l'instance.
État transitoire
État que peut prendre l'objet à un moment donné
Transition
Passage d'un état à l'autre.
Action
création joueur Information placée sur une transition pour commenter l'action provoquée par la
transition.
Condition
[vie=0]
Information placée sur une transition et qui apporte une condition à cette transition.
8 2950 TG PA 00
Exercice 7
Séquence 6
UML, la
modélisation objet
Page 119
Travail à faire
À partir de l’énoncé et en vous aidant de l’annexe 1 qui précise le formalisme utilisé,
reproduire et compléter le schéma fourni ci-dessus représentant le cycle de vie d’une
intervention.
8 2950 TG PA 00
Annexe 1 Diagramme état-transition
Le formalisme suivant permet de définir les états successifs d’un objet et précise les
transitions permettant le passage d’un état à un autre état.
Transition 3 [Etat3]
D é b u t
Transition 4
5. Diagramme de séquence
Séquence 6 Le diagramme de séquence permet de modéliser l'interaction des objets entre eux. Il
UML, la
représente le scénario d'utilisation des objets.
modélisation objet
Question : Quels messages circulent entre les objets ?
Exemple
Quand l'utilisateur se connecte, la classe Joueur est instanciée puis l'instance est incluse
dans le jeu côté serveur. Si l'utilisateur utilise les flèches, l'objet joueur envoie un mes-
sage au serveur pour se déplacer. Le serveur retourne l'affichage de la nouvelle position.
Si l'utilisateur utilise la touche espace, l'objet joueur envoie un message au serveur pour
attaquer. Le serveur retourne l'affichage de la boule qui est lancée. Le serveur contrôle
la vie des joueurs et fait mourir ceux qui sont à 0.
8 2950 TG PA 00
joueur
Séquence 6
UML, la
modélisation objet
Page 121
Analyse du schéma
Ce schéma est incomplet mais donne déjà une vision de ce type de diagramme. Il repré-
sente bien, dans une ligne de temps, les interactions entre l'utilisateur et les interfaces
(se connecter, utiliser les flèches, l'espace...). Les envois de messages entre objets sont
aussi modélisés : se déplacer, attaquer...
8 2950 TG PA 00
Syntaxe
Le diagramme de séquence respecte les règles de présentation suivantes :
Acteur
Il est issu du diagramme de cas d'utilisation. Il interagit sur l'interface (en utilisant
par exemple les touches du clavier).
Objet
Il représente une instance concernée par le cas d'utilisation.
Ligne de vie
Elle symbolise le temps.
Message
Envoi d'un message vers un objet (excepté les flèches qui partent de l'utilisateur
et qui traduisent des interactions avec l'interface). Cela se traduit concrètement
par l'appel d'une méthode à partir de l'objet qui envoi le message, cette
méthode utilisant un objet destinataire du message.
Réponse
Réponse de l'objet suite à un message envoyé. La réponse est directement liée
au message : cela se traduit par la récupération d'une information de l'objet
Séquence 6 destinataire du message.
UML, la
modélisation objet Exercice 8
Page 122 En reprenant le diagramme de séquence donné dans le cours, dites quelles
méthodes doivent être créées et dans quelles classes (donnez juste leur nom et
surtout précisez leur rôle).
Diagramme d'objets
Proche du diagramme de classes, il détaille les instances possibles et les liens entre ces
instances.
Diagramme de communication
Il présente la coopération entre objets.
C'est une autre représentation du diagramme de séquence. Il met en évidence les liens
entre objets.
8 2950 TG PA 00
Diagramme d'activité
Il présente les règles d'enchaînement des activités.
Il représente les activités de l'application ainsi que leur ordre logique et conditionnel.
Diagramme de déploiement
Il modélise l'organisation de la distribution des composants logiciels de l'application.
Diagramme de composants
Il présente l'organisation des ressources au niveau physique.
C'est une représentation des unités (bases de données, fichiers, librairies...) dont l'assem-
blage compose l'application.
Diagramme de packages
Il présente les différents packages de l'application et les liens entre les packages.
Dans le fascicule de TP (mais aussi dans ce cours), vous avez une représentation du dia-
gramme de classes et en même temps des packages (regroupement de classes) et des
liens entre eux.
8 2950 TG PA 00
Synthèse
Séquence 6
UML, la
modélisation objet
Page 124
Diagramme de classes
Il permet de modéliser les données et traitements nécessaires à l’application.
8 2950 TG PA 00
Diagramme d’états
Il permet de modéliser les étapes de vie d’une instance indépendamment des autres.
Diagramme de séquence
Séquence 6
Il permet de modéliser l’interaction des objets entre eux. Il représente le scénario
d’utilisation des objets. UML, la
modélisation objet
Page 125
8 2950 TG PA 00
Séquence 7
Autocorrection
Vous trouverez ici la correction de tous les exercices du fascicule. Les
exercices étant numérotés par séquence, vous les retrouverez classés par
séquence. La séquence 5 n’est pas présente car, compte tenu de sa particu-
larité, elle ne comporte pas d'exercices.
X Contenu
1. Notions fondamentales................................................................................ 128
2. Héritage et polymorphisme ......................................................................... 134
3. Classification, regroupement et opérations associées .............................. 139
4. Persistance des objets .................................................................................. 147
5. UML, la modélisation objet.......................................................................... 149
Séquence 7
Autocorrection
Page 127
8 2950 TG PA 00
1. Notions fondamentales
Exercice 1
Ce premier exercice ne permet pas d'écrire une procédure très utile, mais vous
offre une toute première approche de la syntaxe objet. Cette fois, contrairement à
l'exemple du cours qui manipule un tableau d'objets (tJoueur), vous devez créer une
simple variable, pour vous montrer que c'est bien sûr tout à fait possible. Le reste
ne pose aucun problème : vous remarquez que les propriétés se manipulent comme
des variables (elles peuvent recevoir des valeurs, être affichées…) et les méthodes
se manipulent comme des modules (procédures ou fonctions, suivant le type de la
méthode).
procedure test ()
unjoueur : Joueur
debut
unjoueur ← new Joueur()
unjoueur.nom ← "test"
unjoueur.couleur ← "jaune"
unjoueur.vie ← 50
afficher unjoueur.couleur
unjoueur.avancer(1)
fin
Séquence 7
Autocorrection Exercice 2
Page 128 La méthode estfrappé() est une méthode de la classe Joueur. Puisqu'elle est dans la
classe, elle peut accéder à tout ce qui est dans la classe (public et privé). Cet accès est
direct, sans passer par un objet. Donc tout son contenu est correct : il est permis de
modifier la vie, de la tester, et d'appeler la méthode mourir().
Joueur::estfrappé()
debut
vie ← vie – 2
si vie <= 0 alors
mourir()
finsi
fin
Le second programme est une procédure extérieure à la classe. Elle a donc besoin
d'un objet pour accéder à la partie publique (l'interface) de la classe Joueur.
Puisqu'elle n'a droit qu'au public, elle peut utiliser la propriété nom mais pas la
propriété vie
procedure perdu (unjoueur : Joueur)
debut
unjoueur.vie ← 0 Interdit, car vie est en privé
afficher "le joueur" + unjoueur.nom + " a perdu"
fin
8 2950 TG PA 00
Exercice 3
Le premier algo demandé est un getter. Il suffisait de reprendre la syntaxe du getter
du cours :
fonction getNom () : chaîne
debut
retourner nom
fin
Le second algo demandé est une fonction extérieure à la classe. Elle reçoit d'ail-
leurs en paramètre un objet de type Joueur, pour pouvoir accéder à l'interface de
la classe. Pour accéder au nom et à la couleur qui sont des propriétés privées, il est
donc obligatoire de passer par des méthodes publiques : il faut utiliser les getters
correspondants.
fonction infoJoueur (unjoueur : Joueur) : chaîne
lenom : chaîne
lacouleur : chaîne
message : chaîne
debut
lenom ← unjoueur.getNom()
lacouleur ← unjoueur.getCouleur()
message ← lenom + " est de couleur " + lacouleur
retourner message
fin Séquence 7
Et voici la version en une seule ligne...
Autocorrection
fonction infoJoueur(unjoueur : Joueur) : chaîne
debut Page 129
retourner unjoueur.getNom()+" est de couleur "+unjoueur.getCouleur()
fin
Le troisième algo demandé est la transformation de la fonction précédente en
méthode de la classe Joueur. Comme le dit l'énoncé, il n'y a plus de paramètre.
Effectivement, la méthode étant dans la classe, elle sera utilisée sur une instance de
la classe (donc à partir d'un objet). Dans la méthode, il ne sera plus nécessaire de pas-
ser par les getters car l'accès aux propriétés privées est permis. Ce n'est pas faux mais
non optimisé de passer par les getters. C'est un peu comme faire Nice->Marseille en
passant par Paris… Bref, l'utilisation d'un getter quand on est dans la classe revient
à utiliser un intermédiaire inutile.
Joueur::infoJoueur () : chaîne
debut
retourner nom + " est de couleur " + couleur
fin
Le quatrième algo demandé est la réécriture de la fonction extérieure info-Joueur.
Elle a le même nom que la méthode de la classe, mais ce n'est pas choquant. Elles
sont totalement indépendantes. Cette fois la fonction ne va pas s'occuper de for-
mater le message qui doit être retourné, mais elle va utiliser la méthode infoJoueur
qui s'en charge déjà.
fonction infoJoueur (unjoueur : Joueur) : chaîne
debut
retourner unjoueur.infoJoueur()
fin
8 2950 TG PA 00
Exercice 4
La procédure qui doit être écrite va permettre de créer les objets du tableau tJoueur,
afin de le renvoyer en paramètre de sortie. Il suffit donc de boucler 10 fois, de saisir
les informations nécessaires et de créer chaque objet (chaque case du tableau).
procedure initJoueurs (S tJoueur[1..10] : Joueur)
k : entier
unnom, unecouleur : chaîne
debut
pour k de 1 à 10
afficher "nom du joueur " + k + " = "
saisir unnom
afficher "couleur du joueur " + k + " = "
saisir unecouleur
tJoueur[k] ← new Joueur(unnom, unecouleur)
finpour
fin
Exercice 5
Cette première utilisation d'une méthode statique est très simple. Vous aurez
l'occasion de manipuler par la suite, dans des exercices plus élaborés, des méthodes
statiques autres que de simples getters ou setters.
Séquence 7
procedure affichDepartVie ()
Autocorrection debut
afficher Joueur.getDepartvie()
Page 130 fin
Exercice 6
L'exercice est très similaire à l'exemple donné dans le cours excepté que cette fois,
le code ne se situe pas dans une classe mais à l'extérieur.
procedure combattre (unjoueur : Joueur)
debut
unjoueur.getArme().utiliser()
unjoueur.getArme().ranger()
fin
Remarquez bien l'utilisation de la méthode getArme() sur l'objet unjoueur. Vous
n'avez effectivement pas le droit de récupérer directement la propriété arme
puisqu'elle est en privé. Il faut donc passer par le getter correspondant. Le getter
retournant un objet de type Arme, vous pouvez ensuite lui appliquer les méthodes
publiques utiliser() et ranger().
8 2950 TG PA 00
Exercice 7
Vous avez sans doute remarqué quelques nuances entre la présentation des classes
de cet exercice et celles du cours. Par exemple, l'auteur a tenu à préciser, pour
chaque méthode, si celle-ci avait un rôle de procédure ou de fonction. Malgré les
petites nuances, l'ensemble reste compréhensible. Dans les exercices récapitulatifs
de fin de séquence, vous aurez régulièrement des sujets officiels pour vous habituer
à des syntaxes différentes.
1. Cette première question était une question de cours.
L'encapsulation permet de protéger certains membres de la classe, afin que seules
les méthodes de la classe puissent les manipuler. Les membres privés ne sont donc
pas accessibles à l'extérieur de la classe, alors que les membres publics, oui. En pro-
tégeant ainsi certains membres, l'intégrité de la classe est préservée (en effet, l'accès
à certains membres, sans contrôle, pourrait engendrer des erreurs).
2.
a) Le calcul du coût horaire va se faire en fonction du nombre d'années d'ancienneté
et du taux horaire. Il faut donc dans un premier temps récupérer le nombre
d'années d'ancienneté en utilisant la fonction nbreAnnee qui est donnée en
fin d'annexe. La récupération du taux horaire se fait en passant par le membre
"objet" qualification. Ensuite, une succession de si imbriqués va permettre, suiv-
ant la tranche du nombre d'années, d'appliquer un coefficient sur le taux horaire
afin d'obtenir le coût qu'il ne reste plus qu'à retourner.
Employé::CoûtHoraire () : réel Séquence 7
nbAnnee : entier
cout : réel Autocorrection
debut
nbAnnee ← NbreAnnées(dateembauche) Page 131
cout ← qualification.TauxHoraire()
si nbAnnee > 15 alors
cout ← cout * 1.12
sinon
si nbAnnee > 10 alors
cout ← cout * 1.08
sinon
si nbAnnee > 4 alors
cout ← cout * 1.05
finsi
finsi
finsi
retourner cout
fin
b) Pour obtenir les frais de main-d'œuvre, il suffit de multiplier la durée de l'inter-
vention par le coût horaire qui dépend bien sûr de l'intervenant.
Intervention::FraisMo() : réel
debut
retourner durée * technicien.CoûtHoraire()
fin
8 2950 TG PA 00
c) Pour obtenir les frais kilométriques, il suffit de multiplier la distance (reçue en
paramètre) avec la tarif kilométrique (propriété privée de la classe).
Intervention::FraisKm(dist : réel) : réel
debut
retourner dist * tarifkm
fin
d) Pour retourner la différence entre le montant du contrat (déjà stocké dans une
propriété privée) et le montant réel des interventions, il faut calculer ce dernier. Pour
cela, il faut parcourir toutes les cases remplies du tableau tabinterventions (d'où la
boucle "pour") et, pour chaque intervention, calculer la somme des frais kilomé-
triques et des frais de main-d'œuvre. Le cumul de toutes ces valeurs va donner le
montant réel des interventions.
Contrat::Ecart() : réel
total : réel
k : entier
debut
total ← 0
pour k de 1 à nbintervention
total ← total+tabinterventions[k].FraisKm(cli.Distance())
+ tabinterventions[k].FraisMo()
finpour
retourner montantcontrat - total
Séquence 7 fin
Autocorrection
Navire::estDéchargé() : booléen
debut
retourner (qtéFret = 0)
fin
8 2950 TG PA 00
2. La méthode décharger doit juste diminuer la quantité de fret actuelle de la quan-
tité passée en paramètre. Remarquez le mot "Donnée" qui, vous vous en doutez,
signifie que le paramètre est un paramètre transféré par valeur. Normalement, par
défaut, cela n'est pas précisé. Cependant il arrive que dans les sujets, les rédacteurs
le précisent. Il ne faut pas que cela vous déstabilise.
Navire::décharger(Donnée qté : entier)
debut
qtéFret ← qtéFret - qté
fin
3. Cette méthode est nettement plus complexe que les 2 précédentes. Le but est
de parcourir toutes les zones de stockage, dans l'ordre, et pour chaque zone, de
décharger ce que l'on peut décharger (en fonction de la capacité disponible de la
zone de stockage).
Attention tout de même, il y a 2 pièges intéressants. Si la capacité disponible est suf-
fisante pour réaliser un déchargement total, il faut d'abord enregistrer dans la zone
de stockage la quantité du fret avant de vider le navire. Si les lignes sont inversées,
la quantité de fret ne sera plus correcte. À l'inverse, si le déchargement est partiel,
il faut d'abord vider le la quantité disponible de stockage avant de remplir la zone
de stockage, sinon la quantité disponible ne sera plus correcte
Port::déchargement(Donnée/Résultat unNavire : Navire)
k : entier
debut
k ← 1 Séquence 7
//parcours des zones de stockage pour le déchargement
Autocorrection
tantque non unNavire.estDéchargé() et k <= 20
//contrôle de la quantité de fret
Page 133
si tabStock[k].obtenirCapaDispo() >= unNavire.obtenirQtéFret() alors
//déchargement total
tabStock[k].stocker(unNavire.obtenirQtéFret())
unNavire.décharger(unNavire.obtenirQtéFret())
sinon
//déchargement partiel
unNavire.décharger(tabStock[k].obtenirCapaDispo())
tabStock[k].stocker(tabStock[k].obtenirCapaDispo())
finsi
k ← k + 1
fintantque
fin
8 2950 TG PA 00
2. Héritage et polymorphisme
Exercice 1
Aucune difficulté dans ce premier exercice sur les héritages. Vous constatez juste
que vous avez accès à une méthode publique de la classe ArmeCourte mais aussi à
une méthode publique de la classe Arme, puisque la classe ArmeCourte hérite de la
classe Arme.
procedure utiliseArmeCourte(uneArmeCourte : ArmeCourte, puissance : en-
tier)
debut
uneArmeCourte.frapper(puissance)
uneArmeCourte.ranger()
fin
Exercice 2
Question de cours. La réponse est NON. Ce n'est pas parce que 2 classes ont la même
mère qu'elles accèdent mutuellement à leurs membres publics. Les 2 classes filles
héritent des membres non privés de la mère, et c'est tout. Les "sœurs" entre elles
ne se voient pas.
Séquence 7 Exercice 3
Autocorrection a) Une classe fille peut être mère d'une autre classe.
VRAI. Ce n'est pas parce qu'une classe hérite déjà d'une autre classe, qu'elle n'a pas
Page 134 le droit d'avoir de classes filles qui héritent d'elle.
b) Une classe peut avoir une seule fille.
VRAI. Il n'y a pas de limite, ni inférieure, ni supérieure, dans le nombre de filles d'une
classe.
c) Un membre privé est accessible dans la classe où il se trouve et dans les classes
filles qui héritent de cette classe.
FAUX. Un membre privé reste totalement privé à la classe où il se trouve. Personne
ne peut y accéder en dehors de la classe elle-même, même pas les classes filles.
d) Un membre public est accessible à l'extérieur de la classe, mais en passant par une
instance de cette classe.
VRAI et FAUX. L'affirmation n'est pas assez précise. Effectivement, pour accéder à un
membre public d'une classe lorsque l'on est à l'extérieur de la classe, il faut normale-
ment passer par un objet du type de cette classe (donc une instance de la classe).
Cependant, ce n'est pas le cas si ce membre public est aussi static. Dans ce cas, l'accès
se fera directement par le nom de la classe (voir séquence 1 point 10 du cours). Autre
cas particulier : il est question de l'extérieur de la classe, mais si l'accès se fait à partir
d'une classe fille, alors il est direct, sans passer par une instance.
e) Un membre public est accessible dans les classes filles de la classe concernée, mais
en passant par une instance de cette classe.
FAUX. Comme cela a été dit dans le point précédent, une classe fille accède directe-
ment aux membres publics de sa mère, comme s'ils étaient ses propres membres.
8 2950 TG PA 00
f) Un membre protégé est accessible à l'extérieur de la classe, mais en passant par
une instance de cette classe.
FAUX. Un membre protégé n'est accessible que dans la classe où il se trouve et dans
les classes filles de cette classe. De plus, dans les classes filles, l'accès se fait directe-
ment, sans passer par une instance.
g) Un membre protégé est directement accessible dans une des classes filles de la
classe concernée, comme si c'était directement un de ses membres.
VRAI.
h) Un membre peut être privé et protégé à la fois.
FAUX. Un membre ne peut avoir qu'un niveau de protection possible : public, pro-
tégé ou privé.
i) Une méthode est obligatoirement publique.
FAUX. Une méthode peut très bien être protégée ou même privée. Cela dépend
de son utilité. D'une manière générale, il faut mettre en privé un maximum de
membres, méthodes comprises. Mais comme les méthodes agissent sur l'objet, très
souvent elles sont en public.
j) Une propriété est obligatoirement privée ou protégée.
FAUX. Une propriété peut éventuellement être publique. Cependant, autant il est
classique de trouver des méthodes privées, autant il est fortement conseillé de ne
pas mettre en public les propriétés, afin d'éviter toute modification intempestive
sans contrôle.
k) Un membre peut ne pas avoir de portée (être ni public, ni privé, ni protégé). Séquence 7
FAUX. La portée est obligatoire. Cependant, suivant les langages, il est possible de
Autocorrection
ne pas préciser la portée. Dans ce cas une portée par défaut est appliquée (souvent
la portée privée). En algorithmique, il faut toujours la préciser. Page 135
l) Une classe qui n'a pas de filles ne peut pas avoir de membres protégés.
FAUX. Cela paraît bizarre au premier abord puisque la portée "protégé" n'a d'intérêt
que pour la récupération dans les classes filles. Mais ce n'est pas parce qu'une classe
n'a pas de filles à un moment donné, que plus tard elle ne sera pas récupérée et
héritée pour d'autres applications. Donc, en prévision, il est tout à fait permis de
mettre des membres en protégé.
m) Le constructeur d'une classe fille porte le même nom que le constructeur de la
classe mère.
FAUX. Un constructeur porte toujours le nom de la classe. Donc le constructeur de la
classe fille portera le même nom que la classe fille.
n) Une classe fille n'a pas de constructeur puisqu'elle hérite du constructeur de la
classe mère.
FAUX. Une classe fille a son propre constructeur et hérite aussi du constructeur de la
classe mère. Elle devra en revanche l'appeler explicitement, si elle en a besoin. Seul
son propre constructeur est appelé automatiquement lorsqu'elle est instanciée.
o) Une classe mère accède aux membres publics de ses classes filles.
FAUX. La fille accède à la mère et non le contraire. Cependant, il existe un moyen de
détourner cette interdiction : dans le cas du polymorphisme dynamique d'héritage
ou des méthodes abstraites que vous étudierez plus loin.
8 2950 TG PA 00
Exercice 4
La particularité de cet exemple est l'appel de la méthode "utiliser" en étant dans
la méthode "utiliser". Mais ce sont en réalité deux méthodes différentes : l'une
est dans la classe ArmeLongue, et l'autre est dans la classe mère Arme. La recon-
naissance se faisant par la signature, l'ordinateur saura que vous faites appel à la
méthode de la classe mère.
ArmeLongue::utiliser(distance : entier)
ladistance : entier Appel de la méthode "utiliser"
debut de la classe Mère (car dans la
utiliser() classe ArmeLongue, il n'y a
pas de méthode "utiliser" avec
ladistance ← distance cette signature).
si distance > distancemax alors
ladistance ← distancemax
finsi
...
Fin
Exercice 5
Le fait qu'il existe une méthode "ranger" dans ArmeLongue et une méthode
"ranger" dans Arme ne pose aucun problème dans l'écriture de cette procédure :
il suffit d'appliquer la méthode "ranger" directement sur le paramètre. Comme
Séquence 7 le paramètre est de type ArmeLongue, l'ordinateur saura quelle méthode appeler
(celle de la classe ArmeLongue).
Autocorrection
procédure finAttaque(uneArme : ArmeLongue)
Page 136 debut
uneArme.ranger()
Fin
Pour la deuxième question, la réponse est : rien (excepté le type du paramètre, bien
sûr). En effet, le paramètre va changer de type, mais le contenu de la méthode ne
change pas puisque la classe Arme possède aussi une méthode "ranger".
Exercice 6
1. Cette première question est très simple : il suffit d'appliquer directement la
méthode setRouge() sur le paramètre pour changer son état.
Magasin::rebuter(e/s laPiece : PieceNonAgree)
debut
laPiece.setRouge()
fin
Petite remarque : les paramètres de types complexes (tableaux, objets) sont toujours
transférées par adresse, donc par défaut, ils sont toujours en e/s. Cela signifie que le
paramètre est toujours une référence à un objet et donc toute modification sur le
paramètrès est répercutée sur l'objet correspondant.
8 2950 TG PA 00
2. Cette deuxième méthode doit contrôler toutes les pièces qui se trouvent dans
le tableau, d'où la boucle "pour", qui bien sûr ne balaye pas le tableau dans son
intégralité, mais uniquement les cases remplies, donc les nbPieces premières cases.
Dans la boucle, une fois une pièce sélectionnée, il faut faire deux contrôles : vérifier
qu'elle est à l'état vert (il n'est pas question de repasser à l'orange une pièce qui
est rouge) et que son nombre d'heures est supérieur au nombre d'heures passé en
paramètre.
Magasin::reviser(nbHeures : entier)
k : entier
debut
pour k de 1 à nbPieces
si lesPieces[k].getEtat() = "VERT"
et lesPieces[k].getNbHeures()>nbHeures alors
lesPieces[k].setOrange()
finsi
finpour
fin
8 2950 TG PA 00
Exercice 7
1. Ce constructeur (qui s'appelle init : c'était assez courant dans les anciens sujets
officiels, ça l'est nettement moins mais autant vous habituer à tout) doit valoriser
la propriété privée isbn propre à la classe FTheorique, mais doit aussi valoriser les
propriétés privées de la classe mère. Or, une classe fille n'a pas accès aux membres
privés de la classe mère. La seule solution est donc de passer par la méthode init
de la classe mère. Ce principe est un grand classique de la programmation objet.
Le "super", n'est pas obligatoire (pas d'ambiguïté dans la signature des méthodes).
FTheorique::init(id : Entier, dateDeb : Date, dateFin : Date, duree :
Entier, lib : Chaîne, niveau : Entier, numIsbn : Chaîne, type : Chaîne)
debut
super.init(id, dateDeb, dateFin, duree, lib, niveau, type)
isbn ← numIsbn
Fin
2. Analyse des 2 classes qui héritent de Formation :
a) Il est nécessaire de redéfinir la méthode afficheFormation() dans la classe
FTheorique car cette classe a des attributs spécifiques non pris en charge par la
méthode afficheFormation() de la classe Formation.
En revanche, il est inutile de redéfinir la méthode afficheFormation() dans la classe
FTechnique car cette classe n'a pas d'attribut spécifique par rapport à sa classe mère
(la classe Formation).
Séquence 7 b) Il est possible d'afficher la propriété isbn qui est directement accessible dans
la classe. En revanche, il faut passer par la méthode afficheFormation de la classe
Autocorrection mère pour l'affichage des autres propriétés. Cette fois, on est dans un cas de poly-
morphisme d'héritage (overriding) donc le "super" est obligatoire car il y a bien
Page 138 ambiguïté.
FTheorique::afficheFormation()
Début
super.afficheFormation()
Afficher(isbn)
Fin
3. Pour charger les formations, il faut commencer par récupérer les formations
techniques qui se trouvent dans la table TFORMATION de la base de données. Cette
récupération va se faire par la classe JeuEnregistrements qui permet de récupérer
des lignes correspondant au résultat d'une requête SQL. Une fois cet objet créé, il
faut le parcourir et, à chaque ligne, utiliser les colonnes récupérées pour initialiser
un objet de type FTechnique. Cet objet est alors ajouté au catalogue des formations.
CatalogueFormations::chargeLesFormationsTechniques()
unObjetTechnique : FTechnique
jeu : JeuEnregistrements
ligne : Vecteur
debut
// chargement des formations techniques
jeu.initialiser(“SELECT * FROM TFORMATION WHERE formType = ‘TE’”)
8 2950 TG PA 00
jeu.avancer()
// Boucle sur le jeu d’enregistrements
TantQue non jeu.fin()
ligne ← jeu.extraitLigne()
// Instanciation d'un objet de la classe Technique
unObjetTechnique.init(ligne[0], ligne[1], ligne[2], ligne[3],
ligne[4], ligne[5], ligne[7])
// Rangement dans le vecteur de formations
ajouteUneFormation(unObjetTechnique)
// n-uplet suivant
jeu.avancer()
FinTantQue
jeu.fermer()
fin
4. Cette méthode était très simple : il suffit de parcourir le vecteur des formations
et d'utiliser, sur chaque objet du vecteur, la méthode afficheFormation. Ce qui est
intéressant ici, c'est que vous découvrez la puissance du polymorphisme d'héritage.
En effet, dans le vecteur, il y a des formations techniques et des formations théo-
riques. On utilise le même nom pour la méthode d'affichage et pourtant, suivant si
c'est une formation technique ou théorique, ce ne sera pas la même méthode qui
sera appelée !
CatalogueFormations::afficheLesFormations
i : Entier
Séquence 7
Début
Pour i de 0 à lesFormations.taille() – 1 Autocorrection
lesFormations[i].afficheFormation()
finPour Page 139
Fin
8 2950 TG PA 00
NON : b() est forcément une méthode de la classe CBeta puisque CBeta implémente
l'interface IFB qui contient b(). Par contre c() est dans l'interface IFC donc aussi dans
la classe CAlpha mais CBeta n'a accès aux méthodes publiques de la classe CAlpha
qu'en passant par la propriété alpha. CBeta n'implémente pas IFC donc c() n'est pas
accessible.
4. this.b()
alpha.b()
OUI : this.b() a déjà été expliqué. alpha.b() marche aussi car, en passant par la pro-
priété alpha, on accède à b() qui se trouve dans la classe CAlpha puisque cette classe
implémente IFC qui hérite de IFB.
5. b()
this.alpha.b()
OUI : b() est l'équivalent de this.b() déjà expliqué. this.alpha.b() est l'équivalent de
alpha.b() déjà expliqué.
6. this.alpha.a()
OUI : alpha permet d'accéder à la classe CAlpha qui implémente IFC qui hérite de IFA
donc la méthode a() est forcément redéfinie dans CAlpha.
7. this.d()
this.c()
NON : this.d() est ok car CBeta implémente IFD, par contre this.c() n'existe pas car
CBeta n'implémente pas IFC.
Séquence 7
8 2950 TG PA 00
Lignes de déclarations et instanciations (en Java) :
Avant de donner les réponses, sachez qu'un objet du type d'une interface peut rece-
voir une instance d'une classe qui implémente cette interface.
14. IFC x = new CAlpha()
OUI
15. IFD x = new CAlpha()
OUI
16. IFA x = new CAlpha()
OUI
17. CBeta x = new CAlpha()
NON : CALpha n'est pas une classe qui hérite de CBeta.
18. IFB x = new CBeta()
OUI
19. IFA x = new CBeta()
NON : CBeta n'implémente pas IFA.
20. IFD x = new CBeta()
OUI.
21. IFB x = new CAlpha()
OUI.
Séquence 7
22. CAlpha x = new CAlpha()
Autocorrection
OUI (c'est une instanciation classique).
Page 141
Exercice 2
Le programme commence par un simple parcours de la collection. Puis, le type de
chaque objet de la collection est testé. Il faut bien comprendre que la collection
contient des objets de type Arme. Cependant, comme la classe Arme contient des
sous-types, il est tout à fait possible de trouver des objets de type ArmeLongue ou
ArmeCourte : quoiqu'il arrive, ils sont aussi des objets de type Arme. Donc, le test
permet de vérifier si l'objet de type Arme est plus précisément un objet de type
ArmeLongue. Dans ce cas, on peut appliquer la méthode tirer(), mais pas directe-
ment. Car cette méthode n'existe pas dans la classe Arme. Il faut d'abord caster
l'objet récupéré pour enfin lui appliquer la méthode.
procédure utiliseArme (lesarmes : Collection<Arme>)
k : entier
début
pour k de 1 à lesarmes.cardinal()
si lesarmes.getObjet(k) est ArmeLongue alors
((ArmeLongue)lesarmes.getObjet(k)).tirer()
finsi
finpour
fin
8 2950 TG PA 00
Exercice 3
1. Écrire l’algorithme de la méthode init de la classe OeuvreEnVente
On retrouve ce principe de méthode init qui a été expliqué dans l'exercice 7 de la
séquence 2. Encore une fois, la valorisation des propriétés de la classe mère passe
par l’appel de la méthode init de la classe mère.
Attention, il ne fallait pas oublier d’initialiser la propriété état avec 'L' (c'était explic-
itement demandé dans le sujet). L’oubli de cette initialisation est classique : les étudi-
ants ont tendance à lire trop rapidement les sujets.
procédure OeuvreEnVente.init(unNumero : Entier, unTitre : Chaîne, un-
Prix : Réel)
début
super.init(unNumero, unTitre)
état ← 'L'
prixOeuvre ← unPrix
fin
2. Écrire l'algorithme de la méthode consulter de la classe Catalogue
Le test proposé sur l’indice n'est pas explicitement demandé : c’est juste une sécurité
supplémentaire. Le but ensuite était juste de récupérer le bon objet dans la collec-
tion pour accéder aux getters nécessaires.
procédure Catalogue.consulter(i : Entier)
début
Séquence 7 si i > 0 et i <= lesOeuvres.cardinal() alors
afficher lesOeuvres.donnerObjet(i).getTitre()
Autocorrection
afficher lesOeuvres.donnerObjet(i). getEtat()
sinon
Page 142
afficher 'indice incorrect'
finsi
fin
3. Écrire l’algorithme de la méthode réserver de la classe Catalogue
Le test permet de contrôler si l’œuvre passée en paramètre fait partie de la collec-
tion. Ce qui est intéressant dans cette question, c'est que l'on voit bien, encore une
fois, qu'un objet passé en paramètre est forcément passé par référence. Donc on a
ainsi accès directement à l’œuvre de la collection. Certains d'entre vous ont peut-être
fait une recherche dans la collection pour trouver l’œuvre : inutile et lourd. Le setter
doit être appliqué directement sur l'objet (donc la référence) passée en paramètre.
fonction Catalogue.réserver(uneOeuvre : OeuvreEnVente) : booléen
début
si lesOeuvres.existe(uneOeuvre) alors
uneOeuvre.setReserve()
retourne VRAI
sinon
retourne FAUX
finsi
fin
8 2950 TG PA 00
4. Écrire l'algorithme de la méthode listeLibre de la classe Catalogue
C'est le cas classique de la création en local d'une collection remplie avec certains
objets d'une autre collection. Pour parcourir la collection existante, si vous avez fait
un "pour chaque", ça marche aussi.
fonction Catalogue.listeLibre() : Collection de OeuvreEnVente
maListe : Collection de OeuvreEnVente
i : Entier
début
maListe ← new Collection de OeuvreEnVente()
pour i de 1 à lesOeuvres.cardinal()
si lesOeuvres.donnerObjet(i).getEtat() = 'L' alors
maListe.ajouterObjet(lesOeuvres.donnerObjet(i))
finsi
finpour
retourne (maListe)
fin
Exercice 4
1. Voilà une première question qui est une question de cours. Voici la correction
officielle :
"On dit ici que tous les emplacements sont de dimension fixe. Par conséquent, il
n'y a pas une valeur de l'attribut "dimension" propre à chaque instance de la classe
Séquence 7
Emplacement, mais une même valeur pour toutes les instances. L'attribut "dimen-
sion" à portée classe définit donc une valeur commune à la classe, quelle que soit Autocorrection
l’instance."
Page 143
2. Encore une question de cours... Voici à nouveau la correction officielle :
"La méthode affiche() de la classe de base Emplacement est redéfinie dans les classes
dérivées. Par conséquent, l'appel de la méthode "affiche" sur un objet de la classe
Terrasse provoquera l'appel de la méthode "affiche" de la classe Terrasse, tandis que
l'appel de la méthode "affiche" sur un objet de la classe Etalage provoquera l'appel
de méthode affiche de la classe Etalage.
Si on appelle la méthode "affiche" avec une référence déclarée de classe
Emplacement et pointant sur un objet de classe Terrasse, c'est la méthode "affiche"
de la classe Terrasse qui sera appelée. Idem pour la classe Etalage. Ce mécanisme est
appelé polymorphisme."
J'ajouterai à cette correction officielle, qu'il s'agit plus précisément de polymor-
phisme dynamique d'héritage, appelé aussi "overridding".
Le terme "polymorphisme", ou à défaut "redéfinition", était attendu.
3. Enfin un peu de code !
Pour supprimer un emplacement, il faut d'abord contrôler qu'il existe bien, et pour
cela il y a une méthode bien pratique (la méthode existe). Une fois ce contrôle fait, il
faut utiliser la méthode enlever qui attend en paramètre un indice et non un objet :
voilà pourquoi, dans la parenthèse, on récupère l'indice de l'objet avec la méthode
index.
8 2950 TG PA 00
Plan::supprimeEmplacement(e unEmplacement : Emplacement)
début
si lesEmplacements.existe(unEmplacement) alors
lesEmplacements.enlever(lesEmplacements.index(unEmplacement))
finsi
fin
4. Cette méthode était nettement plus piégeante. Il fallait bien prendre le temps de
réfléchir. Avant tout, si l'emplacement à ajouter n'est pas de type terrasse, alors il
n'y a pas de problème. Voilà pourquoi un booléen est utilisé et initialisé à vrai dès le
début. Il restera à vrai si le premier test (contrôle du type d'emplacement) retourne
faux.
Dans le cas où l'emplacement à ajouter est une terrasse, il faut alors contrôler avec
tous les autres emplacements de type terrasse, si la distance n'est pas trop petite.
Dans ce cas le booléen passe à faux pour éviter d'ajouter.
Plan::ajouteEmplacement(e unEmplacement : Emplacement) : Booléen
i, nbEmp : Entier
OK : Booléen
début
OK ← vrai
si unEmplacement.estType(Terrasse) alors
nbEmp ← lesEmplacements.cardinal()
i ← 1
Séquence 7 tantque (i <= nbEmp) et OK
si lesEmplacements.extraireobjet(i).estType(Terrasse)
Autocorrection
si lesEmplacements.extraireobjet(i).donneDistance
(unEmplacement)< 50 alors
Page 144
OK ← faux
finsi
finsi
i ← i + 1
fintantque
finsi
si OK alors
lesEmplacements.ajouter(unEmplacement)
finsi
retourner OK
fin
Exercice 5
1. Il est possible d'écrire cette méthode de plusieurs façons. Le plus simple était de
remarquer que le code postal des DOM-TOM commence par 97.
Abonnement::localisation() : caractère
début
si codePostal.extraireChaine(1, 2)="97" alors
retourner "D"
sinon
retourner "M"
finsi
fin
8 2950 TG PA 00
2. Cette méthode est simple : il suffit de récupérer le prix de la revue, en utilisant
le getter correspondant, puis lui appliquer un coefficient multiplicateur dans le cas
d'un abonnement en DOM-TOM.
Abonnement::prixAbonnement() : réel
lePrix : réel
début
lePrix ← laRevue.getPrixAbonnementPublic()
si localisation() = "D" alors
lePrix ← lePrix * 1,5
finsi
retourner lePrix
fin
3. Il fallait écrire la classe fille AbonnementMultiple. Cette classe doit apporter, en
plus de la classe mère Abonnement, des informations spécifiques aux abonnements
multiples : le nombre d'exemplaires et le taux de remise. Au niveau méthode, il fal-
lait penser au constructeur qui doit valoriser non seulement les propriétés locales,
mais aussi celles de la mère. Enfin, la méthode prixAbonnement doit être redéfinie
pour prendre en compte les spécificités de l'abonnement multiple.
Classe AbonnementMultiple : Abonnement
privé :
nbreExemplaires : entier
tauxRemise : entier
public : Séquence 7
init(unNuméro:Entier, unNom,unPrénom,uneRaisonSocia
le, unCodePostal,uneVille:Chaîne, uneDateDébut,uneDateFin:Date, Autocorrection
uneRevue:Revue, unNbrExemplaires, unTxRemise:Entier)
prixAbonnement() : réel Page 145
4. Voici un joli cas de polymorphisme d'héritage : pour écrire cette nouvelle méthode,
il est nécessaire de faire appel à la méthode prixAbonnement de la classe mère, qui
comporte la même signature : d'où l'utilisation obligatoire du préfixe "super".
AbonnementMultiple::prixAbonnement() : réel
k
début
prixAbonnement ← super.prixAbonnement() * nbrExemplaires * (100 –
tauxRemise) / 100
fin
5. Il faut construire une nouvelle collection contenant que les abonnements qui
arrivent à échéance. Pour cela, il suffit de parcourir la collection d'abonnements et,
pour chaque abonnement, contrôler l'échéance. La classe technique Date offre une
méthode bien pratique qui permet de compter le nombre de jours entre 2 dates.
Si vous avez utilisé un "pour chaque" pour le parcours de la collection, c'est tout à
fait correct.
Revue::relancerAbonnements() : Collection de Abonnement
mesAbonnements : Collection de Abonnement
i, nbJoursDiff : Entier
lAbonnement : Abonnement
8 2950 TG PA 00
début
pour i de 1 à lesAbonnements.cardinal()
lAbonnement ← lesAbonnements.extraireObjet(i)
nbJoursDiff ← Date.aujourdhui().différence(
lAbonnement.getDateFin())
si nbJoursDiff < 30 et nbJoursDiff > 0 alors
mesAbonnements.ajouterObjet(lAbonnement)
finsi
finpour
retourner mesAbonnements
fin
Exercice 6
1. La consigne était claire : faire la différence entre le prix facturé (qui était acces-
sible en propriété privée de la classe) et le cumul des coûts de main-d'œuvre (acces-
sible par une méthode de la classe).
Projet::margeBrutteCourante() : réel
début
retourner prixFacturéMO – cumulCoûtMO()
fin
2. Pour obtenir ce cumul de coûts, il fallait parcourir la collection de missions et
Séquence 7 multiplier le nombre d'heures effectuées par le taux, lui-même récupéré en fonction
de l'exécutant.
Autocorrection
Projet::cumulCoûtMO() : réel
Page 146 cumulMO : réel
k : entier
uneMission : Mission
taux : réel
début
cumulMO ← 0
pour k de 0 à missions.cardinal() – 1
uneMission ← missions.extraire(k)
taux ← uneMission.getExécutant().getTauxHoraire()
cumulMO ← cumulMO + taux*uneMission.nbHeuresEffectuées()
finpour
retourner cumulMO
fin
3. Cette fois c'est un dictionnaire qu'il fallait manipuler. En récupérant la collection
de clés du dictionnaire, on obtenait les dates contenues dans le dictionnaire. À partir
de là, il était possible, en parcourant la collection de clés, de récupérer les valeurs du
dictionnaire et ainsi de cumuler les heures. Dans la réalité, les langages offrent des
moyens plus rapides pour accéder à l'ensemble des valeurs d'un dictionnaire. Mais
dans les annexes proposées avec ce sujet, ce n'était pas le cas, et l'on est forcé de se
limiter aux outils proposés.
8 2950 TG PA 00
Mission::nbHeuresEffectuées() : entier
k, cumulH : entier
uneClé : Date
lesClés : Collection de Date
début
lesClés ← relevéHoraire.donnerToutesLesClés()
cumulH ← 0
pour k de 0 à lesClés.cardinal()-1
uneClé ← lesClés.extraire(k)
cumulH ← cumulH + relevéHoraire.donnerValeur(uneClé)
finpour
retourner cumulH
fin
Exercice 1
1. L'écriture de cette première méthode ne posait aucune difficulté : c'est un simple
getter sur la propriété nbChamps
Champs::getNbChamps() : entier Séquence 7
debut
retourner nbChamps Autocorrection
fin
Page 147
2. Cette méthode était courte mais intéressante : suivant le type du champ (acces-
sible par la méthode getType) il fallait retourner soit directement la valeur (pour les
champs numériques) soit la valeur entourée de délimiteurs de chaînes. Les guille-
mets étant déjà utilisés, il fallait prendre les côtes ('). D'où la concaténation des deux
côtes autour de la valeur, dans le deuxième cas.
gèreRDV::valeurFormatée(nomChamp:chaîne, valeurChamp:chaîne) : chaîne
debut
si getType(nomChamp) = 'N' alors
retourner valeurChamp
sinon
retourner "'" + valeurChamp + "'"
finsi
fin
3. Cette méthode avait pour but de construire, dans une chaîne, la requête d'inser-
tion nécessaire à l'ajout dans la base de données. La construction de cette chaîne
devait se faire en 2 étapes : d'abord le début de l'insertion avec la toute première
valeur à ajouter, puis une boucle sur les valeurs suivantes, en les séparant par une
virgule. Il ne fallait pas oublier, à la fin, d'ajouter la parenthèse fermante.
La méthode valeurFormatée, précédemment écrite, permettait de concaténer la
valeur avec ou sans les délimiteurs de chaînes.
8 2950 TG PA 00
gèreRDV::ajouter(numéro : chaîne, lesChamps : Champs)
sql : chaîne, i : entier
nom, valeur : chaîne
debut
sql←"insert into RDV values (" + valeurFormatée("numRdv", numéro)
pour i de 0 à lesChamps.getNbChamps() – 1
nom ← lesChamps.getNom(i)
valeur ← lesChamps.getValeur(i)
sql ← sql + "," + valeurFormatée(nom, valeur)
finpour
sql ← sql + ")"
execSql(sql)
fin
8 2950 TG PA 00
5. UML, la modélisation objet
Exercice 1
Ce premier exercice permet juste de créer un petit cas d'utilisation simple, sur la
logique du cas d'utilisation montré dans le cours. Observez juste les extends et
include, donc les dépendances optionnelles ou obligatoires entre les cas d'utilisation.
Séquence 7
Autocorrection
Page 149
Exercice 2
Cet exercice demandait la création non pas du schéma mais du tableau complé-
mentaire d'un cas d'utilisation. En lisant bien le sujet, on s'aperçoit qu'il est en fait
totalement inutile d'avoir des connaissances préalables sur le diagramme de cas
d'utilisation pour savoir répondre à la question. Tout est détaillé dans le sujet ainsi
que les annexes. Cela demandait en fait juste un peu de bon sens.
Voici la correction officielle de cette question ainsi que les commentaires officiels :
Le scénario suivant n’est bien sûr qu’une proposition de solution. Il y a lieu de juger
avant tout de la cohérence du scénario proposé par le candidat, quel que soit le
formalisme utilisé.
8 2950 TG PA 00
Saisie des résultats d'une épreuve d’indice 2
Acteur principal
Membre du jury responsable de l’enregistrement.
Objectifs
Le responsable doit pouvoir effectuer la saisie des résultats le plus simplement
possible.
Préconditions
Le responsable est déjà authentifié.
Postconditions
Les résultats de tous les couples engagés dans cette phase de l’épreuve ont
été renseignés.
Scénario normal
1. Le responsable fournit le numéro de l’épreuve.
2. Le système retourne la catégorie ainsi que le barème de la manche. Il retourne
également la liste des obstacles.
3. Le responsable fournit le code d’engagement du couple qui se prépare à faire
son parcours.
4. Le système retourne le numéro d’ordre de passage et les informations concer-
nant le cavalier et sa monture.
5. Le responsable fournit les fautes commises pour chaque obstacle (nombre de
barres tombées).
6. À l’issue du passage, le responsable indique si le couple est ou non éliminé et
Séquence 7
le temps réalisé.
Autocorrection 7. Le système calcule et retourne le cumul du temps de pénalité correspondant au
nombre de barres tombées (5 secondes par barre) et le temps total.
Page 150 8. Le responsable valide sa saisie.
9. Le responsable reprend à l'étape 3.
Scénarios alternatifs
• À l’étape 5 Le responsable abandonne la saisie des résultats d’un couple.
• À l’étape 6 Le responsable abandonne la saisie des résultats d’un couple.
• À l’étape 7 Le couple est éliminé. Pas de calcul du temps de pénalité et du
temps total.
• À l’étape 8 Le responsable abandonne la saisie des résultats d’un couple.
• À l’étape 9 Le responsable termine la saisie des résultats pour la phase.
On acceptera bien entendu toute solution plus complète intégrant tout ou partie
des améliorations suivantes :
Il est possible de prévoir pour une épreuve un passage automatique d’un couple au
suivant en respectant l’ordre de passage de l’épreuve. On pourra prévoir également
un retour arrière.
On peut également envisager une automatisation du chronométrage intégrée à
l’application (récupération du temps de passage obtenu du chronométrage auto-
matique officiel).
• On peut également prévoir la détermination automatique de l’élimination en
fonction des saisies précédentes (une colonne refus et une colonne chute).
• On peut aussi simplement distinguer le motif de l’élimination.
• On acceptera également une solution consistant à ne saisir que le nombre de
barres tombées pour l’ensemble du parcours.
8 2950 TG PA 00
Exercice 3
Le sujet comporte beaucoup d'explications qui sont bien sûr surtout utiles pour les
questions suivantes du sujet, qui n'apparaissent pas ici.
Il suffisait donc d'ajouter la classe Entretien en gérant correctement les liens avec
EntretienType et avec Vehicule. Les flèches ne sont pas exigées. En revanche, il fallait
penser à placer mesEntretiens et leType sur les liens correspondants.
1..n
Atelier
Véhicule
les Véhicules
lesTypesExistants
1..n
Ent ret ien Type
leType
mesEntret iens
0..n
Entretien
- dateEntretien : date
- nbKmCom pteur : enti er
- comment aire
+ init(uneDate : date, unComm entaire : chaîne, unNb Km : entier, unType : E ntret ienType) Séquence 7
+ get NbKmCompteur() : entier
Autocorrection
Page 151
8 2950 TG PA 00
Exercice 4
Mêmes si ces classes et le diagramme sont issus d'un sujet officiel (celui de 2010), il
n'était pas demandé de créer le diagramme à partir des classes : le diagramme et les
classes étaient entièrement fournis dans le sujet.
Le lien "utilise" traduit le fait qu'une des méthodes (XmlNonLivrées) reçoit en
paramètre un objet de type Distributeur. Vous n'avez certainement pas du voir ce
lien.
Séquence 7
Autocorrection
Page 152
8 2950 TG PA 00
Exercice 5
Comme l'exercice précédent, ces classes sont issues d'un sujet officiel (2009) qui don-
nait aussi le diagramme de classes correspondant.
Cette fois, il y a un cas d'héritage.
Si vous avez fait un lien de composition entre Secteur et Branchement, c'est assez
logique. Idem entre Commune et Secteur. On peut cependant penser qu'un secteur
peut être rattaché à une autre commune...
Commune
- numCom Secteur
- nomCom
1 1..* - numSecteur
+ ajouterUnSecteur - nomSecteur
+ secteurEV laCommune lesSecteurs - espaceVert
+ volumeVannes
+ getNumSecteur
+ perte
+ getNomSecteur
+ anomalie
+ getEspaceVert
lesBranchements
1..*
Compteur Branchement
1 1
- indexAncien
- indexNouveau leCompteur + conso Séquence 7
+ relevé
Autocorrection
Usager Vanne
Page 153
Exercice 6
Là encore, le diagramme et les classes textuelles étaient toutes données dans le sujet
(2009).
La classe association a été implémentée par un dictionnaire (lesJouets) dans la classe
Catalogue. Le diagramme de classes ne mentionne ni les paramètres des méthodes,
ni les constructeurs des classes. Vous devez cependant logiquement avoir trouvé les
constructeurs. En revanche, c'est normal si vous n'avez pas trouvé les paramètres des
méthodes.
Classe Catalogue
privé
année : Chaîne
lesJouets : Dictionnaire de <Jouet, Entier>
// Contient pour chaque jouet du catalogue :
// - en clé, l’objet de la classe Jouet
// - en valeur, la quantité de ce jouet distribuée pour ce catalogue
// public
8 2950 TG PA 00
Constructeur Catalogue (uneAnnée : Chaîne)
Fonction GetAnnée () : Chaîne
Fonction QuantitéDistribuée () : Entier
// Retourne la quantité totale de jouets distribués pour ce
// catalogue.
Fonction StatCatég () : Dictionnaire de <Catégorie, Entier>
// Retourne un dictionnaire contenant pour chaque catégorie de ce
// catalogue :
// - en clé, l’objet de la classe Catégorie
// - en valeur, la quantité de jouets distribués pour cette
// catégorie.
Fin classe
Classe Jouet
privé
numéro : Entier
libellé : Chaîne
catég : Catégorie
tranche : TrancheAge
public
Constructeur Jouet (unNumero : Entier, unLibellé : Chaîne,
uneCatégorie : Catégorie, uneTranche : TrancheAge)
// Instancie un objet Jouet et l’ajoute dans la collection des jouets
Séquence 7
// de sa catégorie
Fonction GetNuméro () : Entier
Autocorrection Fonction GetLibellé () : Chaîne
Fonction GetCatég () : Catégorie
Page 154 Fonction GetTranche () : TrancheAge
Fonction Convient (unAge : Entier) : Booléen
// Retourne vrai si le jouet convient à l’âge passé en paramètre.
Fonction GetInfos () : Chaîne
// Retourne une chaîne contenant : le libellé du jouet, le libellé de
// sa catégorie,
// les âges minimum et maximum de la tranche d’âge lui correspondant.
// Les informations sont séparées par des points-virgules.
Fin classe
Classe Catégorie
privé
code : Entier
libellé : Chaîne
lesJouets : Collection de <Jouet>
// ensemble des jouets de cette catégorie
public
Constructeur Catégorie (unCode : Entier, unLibellé : Chaîne)
Fonction GetCode () : Entier
Fonction GetLibellé () : Chaîne
Procédure AjouterJouet (unJouet : Jouet)
// Ajoute le jouet passé en paramètre à la collection lesJouets
Fin classe
8 2950 TG PA 00
Classe TrancheAge
privé
code : Entier
ageMin : Entier
ageMax : Entier
public
Constructeur TrancheAge (unCode : Entier, unAgeMin : Entier,
unAgeMax : Entier)
Fonction GetCode () : Entier
Fonction GetAgeMin () : Entier
Fonction GetAgeMax () : Entier
Fin classe
Exercice 7
Dans cet exercice issu d'un sujet officiel de l'ancien BTS, il fallait compléter un dia-
gramme existant. Encore une fois, sans connaissances sur ce type de diagramme, les
explications étaient suffisamment claires pour arriver à traiter le sujet. La présen-
tation est d'ailleurs un peu différente de celle présentée dans le cours puisqu'en
plus des états transitoires, les transitions sont représentées par des rectangles
d'informations (en plus de la simple flèche).
Par rapport au schéma donné, et en suivant les explications du sujet, il fallait donc
ajouter la gestion des tâches interrompues (qui peuvent reprendre) et la gestion des
Séquence 7
tâches annulées qui sont ensuite archivées.
Autocorrection
Page 155
8 2950 TG PA 00
Exercice 8
Cet exercice devait juste vous permettre de mieux comprendre le rôle du diagramme
de séquences. Par souci de simplification, ce diagramme n'est d'ailleurs pas la
représentation exacte de ce qui se passe réellement dans le jeu dont il s'inspire.
Les messages sont des méthodes : seules les flèches entre les objets sont analysées.
Voici la traduction de chaque message (vous avez peut-être donné des noms dif-
férents aux méthodes).
Nom méthode Classe Rôle méthode
choixPersonnage() fmChoixJoueur Averti JeuServeur du choix d'un personnage et
donc de l'arrivée d'un nouveau joueur
creationJoueur() JeuServeur Crée une nouvelle instance de Joueur.
creationBoule() Joueur Crée une nouvelle instance de Boule (dès qu'un
joueur est créé, sa boule est créée).
fermetureChoixJoueur() JeuServeur Détruit l'objet frmChoixJoueur.
creationArene() JeuServeur Crée une instance de frmArene
Deplacement frmArene Averti JeuServeur d'un déplacement.
Deplacement() JeuServeur Utilise Joueur pour enregistrer le déplacement.
Joueur retourne la nouvelle position.
joueurDéplacé() JeuServeur Averti frmArene de la nouvelle position du
joueur.
Attaque() frmArene Averti JeuServeur d'une attaque.
Séquence 7 Attaque() JeuServeur Utilise Joueur pour déclencher l'attaque.
Autocorrection lanceBoule() Joueur Utilise Boule pour lancer la boule du joueur.
joueurTouché() Boule Averti Joueur qu'un joueur a été touché par la
Page 156 boule.
joueurTouché() Joueur Averti JeuServeur qu'un joueur a été touché.
joueurTouché() JeuServeur Averti frmArene qu'un joueur a été touché (pour
afficher le joueur blessé).
Mort() Joueur Enregistre la mort et autodétruit l'instance de
Joueur.
avertiMort() Joueur Averti JeuServeur de la mort du joueur.
Mort() JeuServeur Averti frmArene de la mort du joueur (pour
afficher le joueur mort).
8 2950 TG PA 00