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

 

                   privé        

                        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

                  

                   privé        

                        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);

              }

       }

}