INTRODUCTION
A LA PROGRAMMATION ORIENTEE OBJETS
1. UNE
APPROCHE DE LA PROGRAMMATION ORIENTEE OBJETS
(la première partie concerne
l'algorithme, le codage en java commence à la page
13)
1.1. Rappels
1.1.1. Notion de
type
Un type de données est caractérisé par
:
– Un ensemble de valeurs
pouvant être prises par les variables et constantes du type. Cet
ensemble peut être défini en extension (booléen : {vrai, faux} ),
ou en intention (entier : ensemble Z),
– Un ensemble de primitives
(opérateurs, procédures ou fonctions) utilisables sur les variables
et constantes du type.
Par exemple, le
type "chaîne de caractères" peut être défini par :
– L’ensemble de valeurs suivant
: toute combinaison de caractères (éventuellement vide),
– L’ensemble de primitives
suivant :
°
concaténation (opérateur // ou
+),
°
fonction LONGUEUR,
°
etc.
1.1.2. Construction de
types
Un type structuré comporte plusieurs champs
pouvant être d’un type quelconque :
TYPE compte =
STRUCT
numéro : entier
nomclient : chaîne de caractères
solde : réel
FSTRUCT
VAR UnCompte : compte
L’accès à un champ se fait à l’aide de
l’opérateur "." :
UnCompte.numéro
ß12
UnCompte.nomclient ß "Durand"
UnCompte.solde
ß 1000
1.2. Problèmes liés à la construction de
types
1.2.1. Définition
incomplète
L’outil STRUCT permet de définir l’ensemble des
valeurs pouvant être prises par les objets d’un type construit
(ensemble des triplets <entier, chaîne de caractères, réel>
pour notre type compte par exemple). Par contre, rien n’est dit
concernant les opérations applicables aux objets de ce
type.
Le type structuré compte ne permet pas
d’appréhender à lui seul la notion de compte bancaire qu’il est
censé représenter. Il est bien entendu nécessaire de savoir qu’un
compte est caractérisé par un numéro, le nom de son propriétaire et
un solde, mais il est indispensable de compléter cette description
en disant qu’un compte peut être créé, crédité, débité,
etc.
1.2.2. Problème
d’évolution
Le fait de réduire la construction de type à la
seule définition d’une représentation en mémoire a par ailleurs une
conséquence importante : le type ne peut pas évoluer.
Considérons la variable c de type compte et la
séquence d'instructions suivante :
c.numéro
ß12
c.nom ß
"Durand"
c.solde ß
1000
…
AFFICHER "Entrez le montant à débiter : "
SAISIR montant
SI c.solde >= montant
ALORS
c.solde ß
c.solde -
montant
SINON
AFFICHER "Solde insuffisant ! "
FSI
Si le système évolue vers une perception plus
fine du compte bancaire nécessitant la mémorisation des cumuls des
opérations de débit et de crédit, la représentation peut devenir
:
TYPE compte =
STRUCT
numéro : entier
nomclient : chaîne de caractères
cumuldébits, cumulcrédits : réel
FSTRUCT
Le programme ci-dessus doit être totalement
reconsidéré.
1.3. Une première solution : définir des
procédures et fonctions permettant de manipuler les données du
type
Avec ces procédures et fonctions, on aura une
description plus complète du type.
La structure de données pourra évoluer sans
remettre en cause les programmes la manipulant du moment que le
"mode d'emploi" des procédures et fonctions associées à cette
structure reste inchangé.
L'ensemble constitué des données et des
procédures et fonctions manipulant ces données est nommé type
abstrait de données.
Exemple : le type compte
- Description de la structure :
TYPE compte =
STRUCT
numéro :
entier
nomclient : chaîne de caractères
solde : réel
FSTRUCT
– Fonction d'initialisation du
compte
FONCTION initial
(données : num : entier, nom : chaîne de caractères) :
compte
VAR cc : compte
DEBUT
cc.numéro ß
num
cc.nomclient ß
nom
cc.solde ß
0
RETOURNER cc
FIN
– Créditer un compte :
PROCEDURE créditer
(donnée : montant : réel, donnée modifiée : c : compte)
DEBUT
c.solde ßc.solde + montant
FIN
– Débiter un compte :
Même principe que
créditer.
– Connaître le solde d’un compte :
FONCTION solde (donnée
: c: compte) : réel
DEBUT
RETOURNER c.solde
FIN
Cette description fonctionnelle est suffisante
pour utiliser le type comme dans la séquence d'instructions
suivante :
c ß initial
(12, "Durand")
/* c est une variable de type compte */
...
AFFICHER "Saisir le
montant à débiter"
SAISIR
montant
SI solde (c) >= montant alors
ALORS
débiter
(montant, c)
SINON
AFFICHER "Solde insuffisant !"
FSI
2.
ENCAPSULATION, CLASSE ET OBJET
Rien n'empêche
un programmeur d'utiliser le type compte en accédant directement
aux données de la structure, voire en se dispensant
totalement de l'utilisation des fonctions et procédures prévues
(sous prétexte d'écrire un code plus efficace par
exemple).
Il faut donc
pouvoir interdire l'accès en dehors des procédures et fonctions
dédiées à la manipulation des données de la structure, on parlera
de procédures et fonctions membres ou encore de
méthodes.
L'encapsulation consiste à réunir les
données et les procédures et fonctions membres en une seule unité
syntaxique : la classe.
Un objet
est un représentant particulier d'une classe.
Par exemple,
l'objet (1, "Dupont", 15000) ou encore l'objet (2, "Dubois", 12000)
sont des objets de la classe compte.
Chaque objet
possède ses propres données. Par contre, tous les objets d'une même
classe partagent une unique occurrence de chaque
méthode.
2.1.
Déclaration d'une classe
Les classes
sont à déclarer avant les variables.
Syntaxe de
la déclaration habituelle d'une classe :
CLASSE
nomclasse
privé /* cette partie
sera inaccessible en dehors de la classe */
Partie déclarations des données membres de la classe
public /* cette partie sera
accessible en dehors de la classe et constitue l'interface de la
classe */
Partie prototype des méthodes de la classe (procédures et fonctions
membres)
FCLASSE
Exemple : la
classe compte
CLASSE
compte
privé
co_numéro : entier
co_nomclient : chaîne de caractères
co_solde
: réel
public
PROCEDURE
initial (données : numéro : entier, nom : chaîne de
caractères)
PROCEDURE
créditer (donnée : montant : réel)
PROCEDURE
débiter (donnée : montant : réel)
FONCTION
solde () : réel
FCLASSE
Remarques :
-
Une donnée peut être publique (même
si cela pénalise l'évolution de la classe), par exemple on peut
souhaiter que le nom du client soit public.
-
Une méthode peut être privée ; en
effet, il est possible que les méthodes publiques effectuent des
blocs d'instructions communs, dans ce cas on crée une procédure ou
une fonction privée que seules les méthodes de la classe pourront
appeler.
-
Il peut y avoir plusieurs parties
privées ou publiques, par exemple si on souhaite que le nom soit
public, la déclaration de la classe devient :
CLASSE compte
privé
co_numéro : entier
public
co_nomclient : chaîne de caractères
privé
co_solde : réel
public
PROCEDURE initial (données : numéro : entier, nom : ch. de
car.)
PROCEDURE créditer (donnée : montant : réel)
PROCEDURE débiter (donnée : montant : réel)
FONCTION solde () : réel
FCLASSE
-
L'ensemble des parties (données et
méthodes) publiques constitue l'interface et est utilisable
à l'intérieur et à l'extérieur de la classe.
-
Tout membre (donnée ou méthode)
appartenant à une partie privée n'est utilisable qu'à l'intérieur
de la définition d'une méthode de la classe.
2.2. Définition des méthodes
Syntaxe de la définition d'une procédure membre
:
PROCEDURE
nomclasse::nomprocédure
([Paramètres formels avec précision du statut et du
type])
partie déclarative
éventuelle
DEBUT
partie
instructions
FIN
Syntaxe de la définition d'une fonction membre
:
FONCTION
nomclasse::nomfonction
([Paramètres formels avec précision du statut et du
type]) : type
du
résultat
partie déclarative
éventuelle
DEBUT
partie
instructions
FIN
Remarque : chaque méthode reçoit l'objet ayant servi à
son appel en tant que paramètre implicite.
Exemple : définition des méthodes de la
classe compte
– Initialiser un compte :
PROCEDURE compte::initial (données
: numéro: entier, nom : chaîne de caractères)
DEBUT
co_numéro ß
numéro
co_nomclient ß
nom
co_solde ß
0
FIN
– Créditer un compte :
PROCEDURE compte::créditer (donnée
: montant : réel)
DEBUT
co_solde ßco_solde +
montant
FIN
– Débiter un compte :
Même principe que
créditer.
– Connaître le solde d’un compte :
FONCTION compte::solde () :
réel
DEBUT
RETOURNER co_solde
FIN
3.
UTILISATION DES CLASSES
3.1. Instanciation
L'instanciation
d'une classe correspond à la création d'une instance de cette
classe, c'est-à-dire d'un objet. Elle se fait :
. par une déclaration (la variable ou l'objet est alors
statique),
Exemple
:
VAR c :
compte
/* création d'un objet statique */
DEBUT
FIN
3.2. Appel de méthodes
Syntaxe
:
NomObjet.NomMéthode
([paramètres
effectifs])
Exemples (la
classe compte est décrite en haut de la page 5) :
·
Initialiser l'objet c avec le compte
n°1 appartenant à Dupont :
c.initial(1,"Dupont")
·
Créditer le compte de Dupont de 1000
francs :
c.créditer(1000)
3.3. Fonctions d'accès
Pour permettre
l'accès aux données en lecture, on est souvent amené à écrire des
fonctions d'accès puisque généralement les données sont privées. En
fait, une fonction d'accès est ni plus ni moins qu'une
méthode.
Si on souhaite
mettre à jour la donnée, on écrira une procédure et non une
fonction.
Exemple
:
On souhaite pouvoir obtenir d'une part le numéro du compte et
d'autre part le nom du
client ainsi que pouvoir modifier le nom du client.
La classe compte devient
:
CLASSE
compte
privé
co_numéro : entier
co_nomclient : chaîne de caractères
co_solde
: réel
public
PROCEDURE
initial (données : numéro : entier, nom : chaîne de
caractères)
PROCEDURE
créditer (donnée : montant : réel)
PROCEDURE
débiter (donnée : montant : réel)
FONCTION
solde () : réel
FONCTION
nom () : chaîne de caractères
FONCTION
numéro () : entier
PROCEDURE
modif_nom (donnée : nouveau_nom : chaîne de caractères)
FCLASSE
FONCTION compte::nom()
: chaîne de caractères
DEBUT
RETOURNER co_nom
FIN
FONCTION compte::numéro() : entier
DEBUT
RETOURNER co_numéro
FIN
PROCEDURE compte::modif_nom (donnée : nouveau_nom : chaîne de
caractères)
DEBUT
co_nom ß
nouveau_nom
FIN
Remarque : nous
avons uniquement déclaré les prototypes des méthodes entre CLASSE
et FCLASSE mais il est également possible de faire figurer tout le
code des méthodes entre CLASSE et FCLASSE au lieu d'écrire le code
des méthodes après FCLASSE.
3.4. Exercice
a)
Ecrire la classe employe.
Les données
sont : numero:entier, nom:chaîne, adr:chaîne,
salaire:réel.
Il faut prévoir
les méthodes pour :
-
l’initialisation,
- l’affichage
des informations (en 3 méthodes)
-
l’augmentation du salaire en fonction d’un pourcentage (entier)
passé en paramètre.
b) Ecrire un
programme pour le test de la classe. Le programme devra créer 5
employés (les informations seront saisies) puis les afficher et
faire saisir le pourcentage d'augmentation du salaire et enfin
afficher les employés avec le salaire actualisé. Le n° d’employé
est séquentiel.
Correction :
CLASSE employé
Privé
num :
entier
nom :
chaine
sal
: réel
Public
PROCEDURE init(données : numéro:entier, nomemp:chaine,
salemp:réel)
FONCTION affiche_num() : entier
FONCTION affiche_nom() : chaine
FONCTION affiche_sal() : réel
PROCEDURE augmente(données : pourc:entier)
FCLASSE
PROCEDURE
employé::init(données : numéro:entier, nomemp:chaine,
salemp:réel)
DEBUT
num ç
numéro
nom ç
nomemp
sal ç
salemp
FIN
PROCEDURE
employé::augmente(données : pourc:entier)
DEBUT
salçsal+(sal*pourc/100)
FIN
FONCTION
employé::affiche_num() : entier
DEBUT
Retourner num
FIN
FONCTION
employé::affiche_nom() : chaine
DEBUT
Retourner nom
FIN
FONCTION
employé::affiche_sal() : réel
DEBUT
Retourner sal
FIN
ALGO
Test
VAR
Temp : Tableau[1..5] de employé
num : entier
nomemp : chaine
salemp, pourc : réel
DEBUT
Pour num de 1 à 5
afficher ("saisir le nom de l'employé numéro " num);
saisir(nomemp)
afficher("Saisir le salaire de l'employé");
saisir(salemp)
Temp[num].init(num, nomemp, salemp)
Fpour
Pour num de 1 à 5
afficher("Le nom de l'employé : ", Temp[num].affiche_num(), " est
", Temp[num].affiche_nom(), " et son salaire est de ",
Temp[num].affiche_sal()
Fpour
Afficher("Saisir le pourcentage d'augmentation"); Saisir(pourc)
Pour num de 1 à 5
T[num].augmente(pourc)
Afficher("L'employé : ", Temp[num].affiche_num(), ", ",
Temp[num].affiche_nom(), " a un nveau salaire de:",
Temp[num].affiche_sal()
FPour
4.
PROPRIETES DES CLASSES
4.1.
Membre donnée statique
Nous avons vu
que chaque instance d'une classe dispose d'une occurence propre de
chacune des données. Il existe une alternative à cette situation.
Il est possible de définir un membre donnée statique, il n'en
existe alors qu'un seul exemplaire, partagé par toutes les
instances de la classe.
Exemple
:
CLASSE
compte
statique nbcomptes :
entier
co_numéro : entier
co_nomclient : chaîne de caractères
co_solde
: réel
public
PROCEDURE
initial (données : numéro : entier, nom : chaîne de
caractères)
PROCEDURE
créditer (donnée : montant : réel)
PROCEDURE
débiter (donnée : montant : réel)
FONCTION
solde () : réel
FONCTION
nom () : chaîne de caractères
FONCTION
numéro () : entier
PROCEDURE
modif_nom (donnée : nouveau_nom : chaîne de caractères)
FCLASSE
entier compte::nbcomptes ß 0
/* Définition et initialisation */
Remarque
: un membre donnée
statique doit être défini et initialisé en dehors de la déclaration
de la classe.
4.2. Méthode membre
statique
Un membre
donnée statique existe en dehors de toute instance de la classe. De
la même façon, une méthode membre statique peut être appelée
indépendamment de tout objet.
Exemple
:
CLASSE
compte
statique nbcomptes :
entier
co_numéro : entier
co_nomclient : chaîne de caractères
co_solde
: réel
public
…
statique
FONCTION totalcomptes () : entier
FCLASSE
entier compte::nbcomptes ß 0
/* Définition et initialisation */
FONCTION compte::totalcomptes() : entier
DEBUT
RETOURNER nbcomptes
FIN
Affichage du
nombre total :
AFFICHER "Il y a
", compte::totalcomptes(), " comptes"
Remarques
:
- La méthode
membre statique est accessible même s'il n'existe aucune instance
de compte.
- Les méthodes
membres statiques sont le plus souvent utilisées pour manipuler les
membres données statiques. Elles ne peuvent pas désigner
directement une donnée non statique ou une méthode non statique de
la classe (il faut qu'elles créent un objet). En revanche, une
méthode non statique peut utiliser directement une donnée statique
ou une méthode statique de la même classe.
5.
CODAGE EN JAVA
5.1.
Déclaration d'une classe
class
Compte // Déclaration de la classe Compte
d'origine
// Par défaut, toutes les données et méthodes sont considérées
comme étant
// publiques à moins qu'on ne spécifie private devant la donnée ou
devant la méthode.
// En fait, pour améliorer la lisibilité, on spécifie
systématiquement le statut (pour
// l'instant private ou public) comme en pseudo-code.
{
private int co_numero;
private String co_nomclient;
private float co_solde;
public void initial (int numero, String nom)
{
co_numero = numero;
co_nomclient= nom;
co_solde = 0;
// Créditer un
compte :
public void crediter (float montant)
{
co_solde += montant;
}
// Débiter un compte :
même principe que créditer.
// Connaître le solde
d’un compte :
public float solde ()
{
return co_solde;
}
} // Le ;
est facultatif à la fin de la déclaration d'une classe en
Java
5.2. Instanciation
Compte c = new Compte();
// c est créé comme compte
…
En java, l’instanciation d’une classe est
toujours dynamique.
Contrairement à C++, il n’y a aucune
manipulation de pointeurs.
5.3. Appel de méthodes
·
Initialiser l'objet c avec le compte
n°1 appartenant à Dupont :
c.initial (1,
"Dupont");
·
Créditer le compte de Dupont de 1000
francs :
c.crediter (1000);
5.4. Membre donnée
statique
class Compte
private static int nbcomptes;
private int co_numero;
…
private float co_solde;
public
…
}
La donnée statique nbcomptes est implicitement
initialisée à 0.
5.5. Méthode membre statique
class Compte
{
private static int
nbcomptes;
private int co_numero;
…
private float co_solde;
…
public static int totalcomptes()
// On aurait pu avoir
nbcomptes()
{
return nbcomptes;
}
5.6. Déclaration et exemple
d’utilisation d’une classe Compte
Déclaration de la classe
compte
class
Compte
{
private
static int nbcomptes;
private int
co_numero;
private
String co_nom;
private
float co_solde;
public void
initial (int numero, String nom)
{
co_numero = numero;
co_nom=
nom;
co_solde = 0;
nbcomptes++;
}
public void
crediter (float montant)
{
co_solde +=
montant;
}
public
float solde ()
{
return
co_solde;
}
public void affiche()
{
System.out.println("Numéro : "+co_numero);
System.out.println("Nom : "+co_nom);
System.out.println("Solde : "+co_solde);
}
public static int totalcomptes()
{
return nbcomptes;
}
Ceci est à
placer dans un fichier Compte.java et à pré-compiler.
Utilisation de la classe
compte
L'utilisation de la classe Compte
pourra être effectuée dans n'importe quel autre programme. En Java,
tout programme appartient à une classe. Nous allons donc écrire une
classe pour utiliser la classe Compte.
class
Prog1compte // Création d’une
classe pour tester la classe Compte
public static void main (String
argv[
]) //
méthode principale statique
{
Compte compte1=new
Compte(); //
création objet compte1
compte1.initial(1,"dupont");
// appel de la méthode initial
compte1.affiche();
compte1.crediter(1000);
compte1.affiche();
System.out.println("Nombre de
comptes : "+Compte.totalcomptes());
}
};
On pré-compile
et on exécute Prog1compte.
Remarque
:
Si la classe Compte n'est pas dans le même
répertoire, il faut que le classpath comporte le répertoire dans
lequel figure Compte.class.
main : point d'entrée
du programme
On lance l'exécution d'un programme Java en
démarrant une machine virtuelle Java (la JVM) avec, en paramètre,
le nom de la classe de démarrage, laquelle doit forcément; contenir
une méthode main. Une fois que la JVM s'est mise en place,
elle lance le programme proprement dit, par la première instruction
de la méthode main, sans créer d'objet de la classe de démarrage.
Cela fonctionne, car la méthode est déclarée
static.
EXERCICE :
Traduire l'algorithme de la page
9 (classe employé) en Java.
Correction
:
Classe employé :
public
class employé
{
private int
num;
private String nom;
private double sal;
public void init(int numéro, String nom, double
salemp)
{
num=numéro;
this.nom=nom;
sal=salemp;
}
public void augmente(int pourc)
{
sal += sal*pourc/100;
}
public int affiche_num()
{
return num;
}
public String affiche_nom()
{
return nom;
}
public double affiche_sal()
{
return sal;
}
}
Classe Test_employé :
import
java.io.*;
public
class test_employe
{
public static void
main(String argv[]) throws IOException
{
int numéro,pourc,x;
employé e1=new employé();
String nomemp, mess;
double
sal;
// Déclaration du tableau
employé T[]=new employé[6];
for (numéro=1;numéro<=5;numéro++)
{
nomemp=Entree.chaine("Saisir le nom de l'employé");
sal=Entree.reel("Saisir le salaire de l'employé");
// Initialisation du tableau
T[numéro]=new employé();
T[numéro].init(numéro,nomemp,sal);
}
for (numéro=1;numéro<=5;numéro++)
{
mess="Le nom de l'employé numéro " +
T[numéro].affiche_num();
mess+=" est " + T[numéro].affiche_nom() + " et son salaire est de
";
mess+=T[numéro].affiche_sal();
System.out.println(mess);
}
pourc=Entree.entier("Saisir le pourcentage
d'augmentation");
for (numéro=1;numéro<=5;numéro++)
{
T[numéro].augmente(pourc);
}
for (numéro=1;numéro<=5;numéro++)
{
mess="L'employé n°"+T[numéro].affiche_num()+", "+T[numéro].affiche_nom();
mess+=" a un nouveau
salaire de : " + T[numéro].affiche_sal();
System.out.println(mess);
}
}
}