Programmation Par Objets et Langage Java

Partie I. Fondement de la POO (Modularité/Abstraction)




Najib Tounsi
Ecole Mohammadia d'Ingénieurs, Rabat Logo EMI



Année 2017/2018
1ère année Génie Informatique

http://www.mescours.ma/Java/PooJavaPart-1.pdf

Sommaire

Fondement de la POO (Modularité/Abstraction)

Modularité

Programmation par morceaux appelés modules.

Module qui implémente une fonctionnalité (variables et codes sources) bien définie et dont la description est faite à travers une interface précise de sorte qu'on n'a pas besoin de connaître son code pour utiliser un module.

Cette décomposition de logiciel en module

Reste à savoir décomposer : comment?

... Fondement de la POO (Modularité/Abstraction)

Abstraction

L'abstraction tente de réduire et factoriser les détails afin que le programmeur puisse se concentrer sur les concepts importants.

L'abstraction de données, est l'idée importante derrière la notion de type. (ℝ est l'ensemble des réels et ℕ l'ensemble des entiers ... propriétés et manipulations différentes).

Cela permet la programmation sans avoir à connaître les détails (données, codes) de représentation des objets manipulés, détails qui sont définis séparément.

L'important c'est de savoir quoi faire avec un objet.

... Fondement de la POO (Modularité/Abstraction)

Cette séparation entre utilisation et représentation permet

Exemples Introductifs


Programmer sans abstraction de données

Programmer sans abstraction: Exemple 1

Soit le programme qui lit une chaîne de caractère et l'imprime en ordre inverse (image miroir)
(Exemple1)

#include <stdio.h> 
#define MAX 80
main(){
int curChar=0;
char c, t[MAX];

/* On lit et mémorise des caractères */
while ((c=getchar())!='\n') {
t[curChar++] = c;
if(curChar==MAX) break;
}

/* On les imprime en ordre inverse */
while (curChar>0)
putchar( t[--curChar] );
}

A noter: On a choisi un tableau, on manipule directement le tableau. (Autre méthode)
Programme développé par raffinements successifs (on définit les grandes lignes et les dégrossit au fur et à mesure).

Programmer sans abstraction: Exemple 2

Soit maintenant à réutiliser/adapter programme pour tester si la chaîne est un palindrome (le problème est proche).

(Exemple2)

#include <stdio.h> 
#define MAX 80
char t[MAX], s[MAX];
main(){
int curChar=0, i=0;
char c;

/* On lit et mémorise des caractères */
while ((c=getchar())!='\n') {
t[curChar++]=c;
if(curChar==MAX) break;
}

/* On duplique la chaîne en ordre inverse */
i = 0;
while (curChar>0)
s[i++] = t[--curChar];

/* et on l'imprime pour voir (ou on teste l'identité des deux) */
printf("%s %s\n", t, s);
}

A noter: Même remarque. On a utilisé un deuxième tableau pour comparer.

On pourrait tester sur le même tableau... Cela reste un programme dépendant de la notation tableau!

Programmer sans abstraction: Exemple 3

Soit maintenant un programme qui lit une "expression arithmétique" et vérifie si elle est bien parenthésée (parenthèses en correspondances, car images miroirs)

(a * (c + d ) - (-5) )

Réutiliser/modifier le même programme devient plus difficile. Mais le problème est proche. (Exemple3)

    #include <stdio.h> 
    #define MAX 80 
    char t[MAX];
    main(){ 
          int curChar=0; 
          int nbO=0; // nombre parenthèses Ouvrantes 
          int nbF=0; // nombre parenthèses Fermantes
          char c; 
          int i;
          /* On lit et mémorise uniquement les parenthèses */ 
          while ((c=getchar())!='\n') { 
                if (c=='(' || c==')' )
                      t[curChar++] = c; 
                else;
                if(curChar==MAX) break;
           } 
    
         /* On parcourt le tableau est compte les parenthèses */ 
           for (i=0; i<curChar; i++){
                if (t[i] == '(') nbO++;
                else {
                   nbF++;
if (nbF >nbO) break; } } if (i<curChar || nbO != nbF) printf ("Mal Parenthésée :-("); else printf ("bien Parenthésée :-)"); }


A noter: Cela reste un programme dépendant de sa structure de donnée: on est contraint de penser et coder en terme de tableau.


Programmer sans abstraction: Exemple 4

Problème: Vérifier si une expression générale est bien parenthésée.

avec { ( [ < > ] ) }

Reprendre le programme précédent et l'adapter se complique. Il faut chercher une autre idée.

L'idée c'est de mémoriser dans leur ordre d'arrivée les symboles ouvrants déjà rencontrés, et de vérifier si chaque symbole fermant correspond bien au dernier symbole ouvrant rencontré. C'est la notion de pile.

Programme avec abstraction (Pile)

Considérer les données sous la forme d'une pile, et raisonner avec la pile.

Exemple-3 résolu avec la pile

A la place d'un tableau t [MAX] des programmes précédents, on va déclarer une abstraction PILE matérialisée avec un objet p.

(Exemple3-pile).

#include <stdlib.h>
#include "pile.h" // Voir plus loin
#define MAX 20
main(){
int curChar=0;
char c; int b=0;
PILE p; // a la place t[MAX]

p = create ();

/* On lit et empile (resp. depile) les parenthèses
* ouvrantes (resp. fermantes)
*/
while ((c=getchar())!='\n') {
if (c=='(') empiler (p, c);
else if (c==')')
if(estVide(p)) b=1;
else if (sommet(p) != '(') b=1;
else depiler (p);
else;
if(curChar==MAX) break;
}

/* On teste si la pile est vide et b=0. Cas favorable. */

if (estVide(p) && b==0)
printf ("bien Parenthésée :-)");
else printf ("Mal Parenthésée :-(");
}

A noter:

Type Abstrait de Données Pile

La définition de la Pile (en C)

/* 
Type Abstrait PILE de caractères
CopyLeft: :-)
Author: Najib Tounsi
Date: 06/10/04 00:19
Description: TAD pile en langage C
*/

/*
* Structure de données d'une pile
*/

#define MAX 20
typedef struct {
char t[MAX]; /* tableau des éléments empilés */
int top; /* indice du sommet de la pile */
} * PILE;
/* 
* Interface abstraite : Liste des opérations que l'on fait sur une pile
(déclarations des profiles)
*/

PILE create();
/* crée et initialise une pile vide */

void empile(PILE, char);
/* empile le caractère donné */

char sommet(PILE);
/* retourne le caractère au sommet de la pile */

void depiler(PILE);
/* décapite la pile (retire le sommet ) */

int estVide(PILE);
/* teste si la pile est vide */

int estPleine(PILE);
/* teste si la pile est pleine */

/*
* Représentation (Implementation)
* On choisit une structure de données
* et on programme les opérations
*/

/* cf. structure de données plus haut */
/* Programmation des opérations */

PILE create(){
/* On crée une pile et on retourne son adresse */
PILE p = (PILE) malloc(MAX);
p->top = -1;
return p;
}

void empiler(PILE p, char c){
if (!estPleine(p))
p->t[++p->top] = c;
else
printf("Pile Pleine : %c\n", p->t[p->top]);
}

char sommet(PILE p){
return p->t[p->top];
}

void depiler(PILE p){
if (!estVide(p))
p->top--;
else
printf("Pile vide\n");
}

int estVide(PILE p){
return (p->top < 0);
}

int estPleine(PILE p){
return (p->top >= (MAX - 1));
}

Type Abstrait de Données Pile

On a deux parties

Tout ce qui dépend du "tableau" est alors confiné dans les définitions de fonctions. Donc localisé.

Type Abstrait de Données Pile: Intérêt

Abstraction

Modularité

Type Abstrait de Données

On appelle type abstrait de données (TAD) un ensemble d'objets caractérisés par les opérations qui leur sont applicables. Ang. Abstract Data Type (ADT)

Ce qui importe, c'est le concept véhiculé par un objet (et non la structure de données sous-jacente)

/* C'est plus stable */

Spécification d'un TAD


Aspect Syntaxique d'un TAD

Profile des opérations (ou entêtes)

Soit 𝔹 (les booléens), ℙ (les piles) et  ℕ(les entiers)
Type ℙ =
vide : → ℙ
/* Crée une pile vide et la retourne en résultat.*/
sommet : ℙ → ℕ
emPiler : ℙ x ℕ → ℙ
dépiler : ℙ → ℙ
pileVide: ℙ → 𝔹
pilePleine: ℙ → 𝔹
Fin

Aspect sémantique d'un TAD

Spécification du comportement (Fait partie de la description d'un TAD)

cf. Programmation par contrat, langage Eiffel, ou assertions de Java.

Dépiler (Vide ()) = ERREUR
Dépiler (Empiler (p, n)) = p
Sommet (Vide ()) = ERREUR
Sommet (Empiler (p, n)) = n
PileVide (Vide()) = VRAI
PileVide (Empiler (p, n)) = FAUX

Les axiomes traduisent les invariants qui caractérisent les propriétés d’un objet.


Notion de Constructeur.

Opérations permettant d'obtenir (construire) tous objets potentiels d'un certain type.

Les axiomes spécifient l'effet de certaines opérations (dites sélecteurs ou fonctions d’accès) sur d'autres (dites constructeurs)

Constructeurs pour le TAD pile:

Exemples:

☞ Notion importante reprise dans les langages à objets.

Autre exemple de TAD. Une date

Objet Date (constituée de 3 entiers)

Type Date =
Opérations
Date : 𝐍 X 𝐍 X 𝐍 → Date
Jour : Date → 𝐍
Mois : Date → 𝐍
Année : Date → 𝐍
Demain : Date → Date
Axiomes
Jour ( Date ( j, m, a)) = j;
Mois ( Date ( j, m, a)) = m;
Année ( Date ( j, m, a)) = a;
Demain ( Date ( j, m, a)) = si j< 28 alors Date(j+1, m, a)
sinon /* faire le plus dur ...*/
Date(1, m+1, a)
Fin

Remarque : Pour plus d’abstraction, introduire les TADs Jour/Mois/Année au lieu des trois
entiers.

Exercices:

Autre exemple de TAD. Une collection

Type Collection C =
Opérations
Vide : → C
Ajouter : C X 𝐍 → C
Appartient : C X 𝐍 → B Supprimer : C X 𝐍 → C EstVide : C → B

Axiomes
/* Ensemble sans double */
Collection c; x, y entiers */
Appartient ( Vide(), x) = Faux
Appartient ( Ajouter (c, y), x) = si (x=y) alors Vrai
sinon Appartient( c, x)
Supprimer ( Vide(), x) = Vide() Supprimer ( Ajouter (c, y), x) = si (x=y) alors Supprimer (c, x)
sinon Ajouter ( Supprimer (c, x), y)
EstVide ( Vide()) = Vrai
EstVide ( Ajouter (c, y)) = Faux

Fin

Exercice: Collection avec doubles?

Représentation de TAD

Structure de données + algorithmes des opérations

Type Pile: 
Représentation
tableau t [MAX]; /* Tableau pour les éléments de la pile*/
entier top; /* Indice du sommet de la pile +/

opérations
Vide (p) { p.top = -1; }
Empiler (p, n) { p.top = p.top+1;
p.t[p.top] = n;
}
Depiler(p) { p.top = p.top-1; } Sommet(p) { return p.t[p.top]; } PilePleine(p) { return p.top = (MAX-1); } PileVide(p) { return p.top < 0; }
fin


Remarque: Prise en compte des cas d'exceptions dans les algorithmes (spécification. sans Pré/post):

On teste les cas d'erreurs à l'intérieur des opérations

 Empiler (p, n) {
if top<(MAX)
p.top = p.top+1;
p.t[p.top] = n;
else
erreur("pile pleine");
endif
}

erreur() est une fonction donnée de traitement d'erreurs.

(Mais voir plus tard les exceptions)


Exercices:

  1. Programmer le reste des opérations.
  2. Choisir et programmer la représentation des TADs des exercices précédents.


En Résumé

Au lieu de programmer avec t [ expression ], top++, ... on fait empiler (p, c), sommet (p) etc.

On ne connaît pas comment est fait l'objet p. On a uniquement besoin de savoir avec quoi le manipuler (quelles opérations?). On appelle cela Interface de l'objet


Notion d'encapsulation