programmation orientée objet, introduction à Ada 95

Transcription

programmation orientée objet, introduction à Ada 95
Introduction à Ada 95
1
Programmation par Objets
• Approche "Type Abstrait de Données"
• Le système est décrit en terme d'"objets"
représentant des entités réelles ou abstraites du
domaine
• Chaque objet contient ses propres données et un
ensemble d'opérations pour manipuler ces données
• La structure et le comportement d'un objet sont
décrits par son type
• Ada supporte cette approche avec les notions de
type et de paquetage
2
Extension de type
• Ada 95 permet de créer un nouveau type à
partir d'un type donné en lui ajoutant des
composants
• Ada 95 s'appuie pour cela sur le concept de
type dérivé
• Pour pouvoir être étendus, les types doivent
être des types articles déclarés étiquetés
("tagged")
3
Exemple
type Compte is tagged
record
titulaire:Unbounded_String;
numero:String( 1..5 );
solde:Float;
end record;
type CompteRemunere is new Compte with
record
taux:Float;
end record;
4
Extension de type
• Les opérations du type père sont héritées
par les types fils dérivés.
• Elles sont appelées opérations primitives
• Ce sont les opérations déclarées dans le
paquetage du type père ou bien qui ont le
type père en paramètre ou en résultat.
5
Type article avec partie variante
vs type étiqueté (1/2)
type Genre is( masculin,feminin );
type Personne( sexe:Genre ) is
record
naissance:Date;
case sexe is
when masculin => barbe:Boolean;
age :Natural;
when feminin => enfants:Natural;
end case;
end record;
6
Type article avec partie variante
vs type étiqueté (2/2)
type Genre is( masculin,feminin );
type Personne is tagged
record
naissance:Date;
end record;
type Homme is new Personne with
record
barbe:Boolean;
age :Natural;
end record;
type Femme is new Personne with
record
enfants:Natural;
end record;
7
Types avec
partie variante
pierre:Personne(masculin);
jeanne:Personne(feminin);
pierre:=(masculin,(12,2,19
50),false,34);
pierre.barbe:=true;
Types étiquetés
x:Personne:=( 2,11,1962 );
pierre:Homme
:=((12,2,1950),false,34);
jeanne:Femme;
-- conversion Homme->Personne
x:=Personne( pierre );
-- conversion Personne->Homme
pierre:=( x with true,age=>42 )
8
Types avec
partie variante
procedure put
(p:in Personne)is
begin
put("date de naissance:(");
put( p.naissance.jour,2 );
put( p.naissance.mois,2 );
put_line(p.naissance.an,2);
case p.sexe is
when masculin =>
put( "age=" );put(p.age,2);
put( "barbe=" );
put(Boolean'image(p.barbe));
when feminin =>
put(p.age,2);put(" enfants");
end case;
end put;
Types étiquetés
procedure put(p:in Personne)is
begin
put( "date de naissance:(" );
put( p.naissance.jour,2 );
put( p.naissance.mois,2 );
put_line( p.naissance.an,2 );
end put;
procedure put( p:in Homme )is
begin
put( Personne(p) );
put( "age=" );put( p.age,2 );
put( "barbe=" );
put( Boolean'image(p.barbe) );
end put;
9
Approche Orientée Objet
• Introduction du concept d'héritage
• De nouveaux types peuvent être introduits par
extension de types existants
• L'ensemble des types forment une hiérarchie
• Tout nouveau type créé par héritage :
– dispose des données et opérations du type père
– peut définir de nouvelles opérations et/ou données
– peut redéfinir les opérations héritées
10
Compte
titulaire
solde
opérations
héritées par les
types fils
numero
crediter
champ
ajouté
debiter
CompteRemunere
CompteSecurise
taux
debiter
opération
redéfinie
calculInterets
opération
ajoutée
11
package Comptes is
--------- type Compte ------------------------type Compte is tagged
record
titulaire
: String( 1..10 );
solde
: Float;
numero
: String( 1..5 );
end record;
procedure debiter( C: in out Compte;S: in Float );
procedure crediter( C: in out Compte;S: in Float );
procedure initialiser
( T: in String;S: in Float;N:in String;C: out Compte );
-------- type CompteRemunere ----------------type CompteRemunere is new Compte with
record
taux
:Float;
end record;
function calculinterets( C:CompteRemunere ) return Float;
procedure initialiser
(T:in String;S:in Float;N:in String;taux:in Float;C:out CompteRemunere );
-------- type CompteSecurise ----------------soldeInsuffisant:exception;
type CompteSecurise is new Compte with null record;
procedure debiter( C: in out CompteSecurise;S: in Float );
end comptes;
12
package body comptes is
---------- type Compte ------------------procedure debiter( C: in out Compte;S: in Float ) is
begin
C.solde:= C.solde-S;
end debiter;
procedure crediter( C: in out Compte;S: in Float )is
begin
C.solde := C.solde+S;
end crediter;
procedure initialiser
( T:in String;S:in Float;N:in String;C:out Compte )is
begin
C := ( T,S,N );
end initialiser;
13
---------- type CompteRemunere --------------------------function calculinterets( C:CompteRemunere ) return Float is
begin
return C.solde*C.taux;
end calculinterets;
procedure initialiser
( T: in String;S: in Float;N: in String;
taux: in Float;C: out CompteRemunere )is
begin
Hérite de la procédure
initialiser( T,S,N,C ); C.taux:=taux; initialiser de Compte
end initialiser;
---------- type CompteSecurise --------------------------procedure debiter( C:in out CompteSecurise;S:in Float )is
begin
if C.solde>=S
C est "une sorte de"
then C.solde := C.solde-S;
Compte
else raise soldeinsuffisant;
end if;
exception
when soldeInsuffisant=>
put_line( "Votre solde est insuffisant" );
end debiter;
end comptes;
14
with comptes;use comptes;
procedure clientcomptes is
courant:Compte;
remunere:CompteRemunere;
securise:CompteSecurise;
begin
initialiser( "Victor",200.0,"34567",courant );
initialiser( "Pierre",0.0,"78231",0.037,remunere );
initialiser( "Daniel",150.0,"77553",securise );
debiter( courant,100.0 );
crediter( remunere,650.0 );
debiter( securise,200.0 );
debiter( remunere, 300.0 );
end clientcomptes;
15
Héritage
•
Ajoutons la déclaration de la procédure put au type Compte dans
comptes.ads
procedure put(C:in Compte);
•
Ajoutons la déclaration du corps de la procédure put au type Compte dans
comptes.adb
procedure put(C:in Compte)is
begin
put( "titulaire : " );put( C.titulaire );new_line;
put( "solde
: " );put( C.solde,5,1,0 );new_line;
put( "numero
: " );put( C.numero );new_line;
end put;
16
with comptes;use comptes;
with Ada.Text_Io;use Ada.Text_Io;
procedure clientcomptes is
courant:Compte;
remunere:CompteRemunere;
securise:CompteSecurise;
begin
initialiser( "Victor",200.0,"34567",courant );
put(courant);new_line;
initialiser( "Pierre",0.0,"78231",0.037,remunere );
put( remunere );new_line;
initialiser( "Daniel",150.0,"77553",securise );
put( securise );new_line;
put_line( "-----------------" ); la procédure put de Compte
debiter( courant,100.0 );
est exécutée car remunere et
put(courant);new_line;
securise sont d'un type
dérivé du type étiqueté père
crediter( remunere,650.0 );
Compte
put( remunere );new_line;
debiter( securise,200.0 );
put( securise );new_line;
17
end clientcomptes;
Polymorphisme (1/2)
•
•
•
•
•
A chaque type étiqueté T correspond un type Classe T'Class
Un type T'Class est l'union de tous les types dérivés à partir de T
(y compris T)
Par exemple, Compte'Class inclut les types Compte,
CompteRemunere et CompteSecurise
Le type Compte'Class remplace donc tous les types inclus
Par exemple, une procédure print dans clientComptes est un
objet polymorphe (le type réel du paramètre formel est inconnu).
procedure print( C: in Compte'Class ) is
begin put( C );new_line; end print;
print pourrait s'appliquer aux paramètres inclus dans Compte'Class
print( courant );
print( securise );
print( remunere );
18
Polymorphisme (2/2)
•
Le choix de l'opération print est réalisé dynamiquement
•
Cela est rendu possible car chaque type étiqueté inclus un "tag"
invisible qui renseigne Ada sur le type effectif du paramètre
•
On appelle liaison dynamique le mécanisme mis en oeuvre pour
utiliser un sous-programme en l'adaptant aux paramètres effectifs
19
Polymorphisme : exemple 1 (1/2)
with comptes,Ada.Text_Io;use comptes,Ada.Text_Io;
Type non
procedure clientcomptes_acces is
contraint=>
procedure print( C: in Compte'class ) is
erreur à la
begin put( C );new_line; end print;
compilation
courant : Compte;
remunere: CompteRemunere; securise: Comptesecurise;
type Tabcomptes is array( 1..3 ) of Compte'class;
begin
initialiser( "Victor",200.0,"34567",0.045,courant );
initialiser( "Pierre",0.0,"78247",0.037,remunere );
initialiser( "Daniel",150.0,"77553",securise );
declare
t:Tabcomptes:=( courant,remunere,securise );
begin
for i in t'range loop print( t(i) ); end loop;
end;
end clientcomptes_acces;
20
Polymorphisme : exemple 1 (2/2)
•
Dans l'exemple précédent, on constate que les types à échelle de classe
ne sont intéressants qu'avec le typage des paramètres formels
•
Comment créer un tableau dont les éléments appartiennent à la même
hiérarchie de types ?
•
La solution précédente provoque une erreur à la compilation : le type
des éléments du tableau est non contraint puisqu'ils peuvent être de
type différent ( Compte, CompteSecurise, …).
21
Polymorphisme : exemple 2 (1/3)
•
En Ada 83, les objets créés dynamiquement ne pouvaient être
référencés qu'à travers des variables de type access
•
Ada 95 nous donne la possibilité de manipuler une variable à travers
son pointeur à condition de déclarer un type accès général
type Ptr_Compte is access all Compte'Class;
p: Ptr_Compte;
•
Pour pouvoir affecter l'adresse d'une variable du type Compte'Class, il
suffit de la déclarer comme "aliased"
remunere: aliased CompteRemunere;
•
Pour affecter une valeur à un pointeur, il n'est plus nécessaire de construire un
objet dynamiquement (opérateur new), il est possible d'accéder à l'adresse
d'un objet statique par l'attribut Access
p:=remunere'Access;
22
Polymorphisme : exemple 2 (2/3)
with comptes, Ada.Text_Io;use comptes, Ada.Text_Io;
procedure clientcomptes_acces is
procedure print(C:in Compte'class) is
begin put(C);new_line; end print;
la taille des
courant : aliased Compte;
composants
remunere: aliased CompteRemunere;
est connue
securise: aliased CompteSecurise;
type Ptr_Compte is access all Compte'Class;
type Tabcomptes is array(1..3) of Ptr_Compte;
t:Tabcomptes;
begin
initialiser( "Victor",200.0,"34567",0.045,courant );
initialiser( "Pierre",0.0,"78231",0.037,remunere );
initialiser( "Daniel",150.0,"77553",securise );
t(1):=courant'access;t(2):=remunere'access;t(3):=securise'access;
for i in t'range loop
print( t(i).all );
end loop;
end clientcomptes_acces;
23
Polymorphisme : exemple 2 (3/3)
•
Contrairement à l'exemple 1, le compilateur dispose de toutes les
informations pour allouer l'espace mémoire aux composants du
tableau.
•
Le tableau t contient donc des références sur des objets de type
différent.
•
Le type réel de l'objet n'est reconnu qu'au moment de l'exécution
24
Abstraction de données
•
•
•
•
Mécanisme pour cacher à l'utilisateur la représentation d'un type donné
Pour écrire un programme utilisant un tel type, l'utilisateur ne peut plus
se fonder que sur l'interface du type
Ce concept facilite la maintenance et la fiabilité du logiciel
Le type est alors déclaré :
tagged private
ou bien
new Père with private
•
Exemple
type Compte is tagged private;
type CompteRemunere is new Compte with private;
25
package Comptes is
type Compte is tagged private;
procedure debiter( C:in out Compte;S:in Float );
procedure crediter( C:in out Compte;S:in Float );
type CompteRemunere is new Compte with private;
function calculinterets( C:CompteRemunere ) return Float;
type Comptesecurise is new Compte with private;
procedure debiter( C:in out CompteSecurise;S:in Float );
private
type Compte is tagged
record
titulaire
:String(
solde
:Float;
numero
:String(
end record;
type Compteremunere is new
record
taux
:Float;
end record;
type CompteSecurise is new
end comptes;
1..6 );
1..5 );
Compte with
Compte with null record;
26
Types abstraits
•
Un type est abstrait lorsque, bien qu'il ne représente aucun objet réel, il
regroupe l'ensemble des caractéristiques (données+opérations)
communes à tous les types dérivés présents et à venir
•
Un type abstrait peut posséder des sous-programmes abstraits
•
On garantit ainsi que tout nouveau type dérivé ajouté à la hiérarchie
implantera les sous-programmes du type abstrait. Le compilateur sera
en mesure de signaler cet oubli.
•
On favorise ainsi le polymorphisme
27
package Comptes is
--- Compte -----------------type Compte is abstract tagged private;
procedure debiter( C:in out Compte;S:in Float ) is abstract;
procedure crediter( C:in out Compte;S:in Float );
procedure put( C:in Compte );
--- CompteRemunere ----------type CompteRemunere is new Compte with private;
function calculInterets( C:CompteRemunere) return Float;
procedure initialiser( T:in String;S:in Float;N:in String;
taux:in Float;C:out CompteRemunere );
procedure debiter( C:in out CompteRemunere;S:in Float );
--- CompteSecuriseRemunere --soldeinsuffisant:exception;
type CompteSecuriseRemunere is new CompteRemunere with private;
procedure debiter( C:in out CompteSecuriseRemunere;S:in Float );
--- CompteSecurise ----------type CompteSecurise is new Compte with private;
procedure initialiser( t:in String;s:in Float;n:in String;
C:out CompteSecurise );
procedure debiter( C:in out CompteSecurise;S:in Float );
28
private
type Compte is abstract tagged
record
titulaire
:String(1..6);
solde
:Float;
numero
:String(1..5);
end record;
type CompteRemunere is new Compte with
record
taux
:Float;
end record;
type CompteSecurise is new Compte with null record;
type CompteSecuriseRemunere is new CompteRemunere
with null record;
end comptes;
29
with Ada.Text_Io;use Ada.Text_Io;
with Ada.Float_Text_io;use Ada.Float_Text_io;
package body Comptes is
------------------ type Compte -----------------procedure crediter( C: in out Compte;S: in Float )is
begin
C.solde := C.solde+S;
end crediter;
procedure put( C:in Compte )is
begin
put( "titulaire : " );put( C.titulaire );new_line;
put( "solde
: " );put( C.solde,5,1,0 );new_line;
put( "numero
: " );put( C.numero );new_line;
end put;
30
--------type CompteRemunere -------------------------function calculinterets( C:CompteRemunere ) return Float is
begin
return C.solde*C.taux;
end calculinterets;
procedure initialiser( T:in String;S:in Float;N:in String;
taux:in Float;C:out CompteRemunere )is
begin
C:=( T,S,N,taux );
end initialiser;
procedure debiter( C:in out CompteRemunere;S:in Float ) is
begin
C.solde:=C.solde-S;
end debiter;
--------type CompteSecuriseRemunere -------------------procedure debiter(C:in out CompteSecuriseRemunere;S:in Float)is
begin
if C.Solde>=S
then C.Solde := C.Solde-S;
else raise Soldeinsuffisant;
end if;
exception
when soldeInsuffisant=>put_line("Votre solde est insuffisant");
31
end debiter;
--------- type CompteSecurise -------------------------procedure initialiser( T:in String;S:in Float;N:in String;
C:out CompteSecurise )is
begin
C:=( T,S,N );
end initialiser;
procedure debiter( C:in out CompteSecurise;S:in Float )is
begin
if C.Solde>=S
then C.Solde := C.Solde-S;
else raise soldeInsuffisant;
end if;
exception
when soldeInsuffisant=>
put_line( "Votre solde est insuffisant" );
end debiter;
end comptes;
32
Remarques
•
Les opérations abstraites sont nécessairement déclarées et définies
dans tous les types fils
• On ne peut pas déclarer des objets d'un type abstrait
C:Compte; -- illégal
33
with Comptes;use Comptes;
with Ada.Text_Io;use Ada.Text_Io;
procedure clientcomptes is
procedure print(C:in Compte'Class) is
begin
put(C);new_line;
end print;
remSec:CompteSecuriseRemunere;
remunere:CompteRemunere;
Securise:CompteSecurise;
begin
initialiser( "Victor",200.0,"34567",0.045,remSec );
put( remSec );new_Line;
initialiser( "Pierre",0.0,"78231",0.037,remunere );
put( remunere );new_Line;
initialiser( "Daniel",150.0,"77553",securise );
put( securise );new_Line;
debiter(remSec,100.0); put( remSec );new_Line;
crediter( remunere,650.0 ); put( remunere );new_line;
debiter( securise,200.0 ); put( securise );new_Line;
print( remSec );
end clientcomptes;
34
Retour sur String (1/3)
•
Comment se libérer de la contrainte sur la taille des chaînes de
caractères
35
Retour sur String (2/3)
with Ada.Strings.Bounded;
package Comptes is
package Chaines is
new Ada.Strings.Bounded.Generic_Bounded_Length( 15 );
use chaines;
………
procedure initialiser(t:in BOUNDED_STRING;s:in Float;
n:in BOUNDED_STRING;taux:in Float;
c:out Compteremunere);
procedure initialiser(t:in BOUNDED_STRING;s:in Float;
n:in BOUNDED_STRING;
c:out CompteSecurise);
type Compte is tagged
record
titulaire
:BOUNDED_STRING;
solde
:Float;
numero
:BOUNDED_STRING;
36
end record;
Retour sur String (3/3)
procedure Clientcomptes_Acces is
initialiser( To_Bounded_String("Victorine"),200.0,
To_Bounded_String("3456725"),0.045,remsec );
initialiser( To_Bounded_String("Pierre"),0.0,
To_Bounded_String("782"),0.037,remunere );
initialiser( To_Bounded_String("Dan"),150.0,
To_Bounded_String("77553"),securise );
37
Etude de cas
On veut manipuler dans un même programme les objets suivants :
– des images caractérisées par leur couleur et leur position (respectivement
du type Couleur et Position).
Parmi ces images, on trouve:
– des photos, caractérisées par leur taille et que l’on pourra compresser,
– des carrés et des hexagones définis par le nombre et la longueur de leurs
cotés
– et plus généralement des polygones réguliers définis par le nombre de
cotés et la longueur de leurs cotés et dont on pourra calculer la superficie
et le périmètre
On veut pouvoir afficher à l’écran chacun de ces objets et les déplacer
Proposer l’architecture qui vous paraît la plus adaptée. Elle sera exprimée en
UML.
On ne s’intéressera donc pas au codage des méthodes mais seulement à leur
déclaration.
38
Image
teinte:Couleur
place:Position
put(){abstract}
deplacer( int )
PolyReg
nbCotes:int
longueur:int
surface():float
perimetre():int
put()
Carre
Photo
taille : float
compresser( float)
put()
Hexagone
surface():float
39
le type Image
package images is
type Couleur is ( rouge,jaune,bleu,blanc,vert,orange,violet);
type Position is
record
x : Natural;
y : Natural;
end record;
type Image is abstract tagged
record
teinte : Couleur;
place : Position;
end record;
procedure put( p: in Image ) is abstract ;
procedure deplacer(p: in out Image;t: in Natural);
end images;
40
Le type PolyReg
with images;use images;
package polyregs is
type PolyReg is new Image with
record
nbCotes
: Positive;
longueur : Positive;
end record;
function surface( f: PolyReg ) return Float;
function perimetre( f: PolyReg ) return Natural;
procedure put( p: in PolyReg );
end polyregs;
41
Le type Carre
with polyregs; use polyregs;
package carres is
type Carre is new PolyReg with NULL record;
function surface( c: Carre ) return Float;
end carres;
42
Le type Hexagone
with polyregs;use polyregs;
package hexagones is
type Hexagone is new PolyReg with
null record;
end hexagones;
43
Le type Photo
with images;use images;
package photos is
type Photo is new Image with
record
taille : Float;
end record;
procedure compresser( p: in out Photo;coef: in Float );
procedure put( p: in Photo );
end photos;
44
Corps du package images
with Ada.Text_io;use Ada.Text_io;
with Ada.Integer_Text_io;use Ada.Integer_Text_io;
package body images is
procedure deplacer( p: in out Image;t: in Natural is
begin
put("nouvelle place de la figure: ");
put("(");put(p.place.x+t,3);put(",");
put(p.place.y+t,3);put(")");
end deplacer;
end images;
45
Corps du package polyregs(1/2)
with Ada.Numerics.Generic_Elementary_Functions;
with images;use images;
package body polyregs is
package tangente is
new Ada.Numerics.Generic_Elementary_Functions( Float );
use tangente;
function surface( f: PolyReg ) return Float is
begin
return Float(f.nbCotes*f.longueur*f.longueur)/
4.0*tan( 3.14/Float(f.nbCotes) );
end surface;
function perimetre( f: PolyReg ) return Natural is
begin
return f.nbCotes*f.longueur;
end perimetre;
end polyregs;
46
Corps du package polyregs
(2/2)
procedure put( p: in PolyReg ) is
begin
put( "couleur = " );
put( Couleur'image( p.teinte ) );
put( ", place = " );put("(");
put( p.place.x,3 );put( "," );
put( p.place.y,3 );put(")");
put( ", nbCotes = " );put( p.nbCotes,2 );
put( "," );
put( ", longueur= " );put( p.longueur,3 );
new_line;
end put;
47
Corps du package carres
package body carres is
function surface( c: Carre ) return Float is
begin
return Float(c.longueur*c.longueur);
end surface;
end carres;
48
Corps du package photos
with Ada.Text_io;use Ada.Text_io;
with Ada.Float_Text_io;use Ada.Float_Text_io;
with Ada.Integer_Text_io;use Ada.Integer_Text_io;
package body photos is
procedure compresser( p: in out Photo; coef: in Float ) is
begin
put( "compresser : nouvelle taille de la photo= " );
put( p.taille*coef, 3,2,2 );
end compresser;
procedure put( p:in Photo ) is
begin
put( "couleur = " );put( Couleur'image( p.teinte ) );
put( ", place = " );put("(");put( p.place.x,3 );put( "," );
put( p.place.y,3 );put(")");
put( ", taille=" );put( p.taille,2,2,2 );new_line;
end put;
49
end photos;
with
with
with
with
with
with
images;use images;
carres;use carres;
hexagones;use hexagones;
photos;use photos;
Ada.Text_io;use Ada.Text_io;
Ada.Float_Text_io;use Ada.Float_Text_io;
procedure client_poo is
c:Carre:=( rouge,(25,50),4,15 );
h:Hexagone:=( bleu,(10,80),6,10 );
p:Photo:=( blanc,(23,46),60.0 );
s:Float;
procedure afficher( f: in Image'class ) is
begin
put ( f );
end afficher;
begin
afficher( c );s:=surface( c );put( s,2,2,2 );new_line;
afficher( h );s:=surface( h );put( s,2,2,2 );new_line;
afficher( p );deplacer( p,10 );afficher( p );new_line;
put( c );put( h );put( p );new_line;
end client_poo;
50
Ajout d'un nouveau type
On souhaite ajouter le type Cercle est défini par son rayon, sa couleur et
sa position. On doit pouvoir les déplacer et les afficher.
On décide donc de le faire hériter du type Image.
La méthode put de Image étant abstraite, il faut la définir dans le type
Cercle pour que le compilateur ne provoque pas d'erreur
Cercle
rayon:float
put()
51
Le package cercles (1/2)
with images;use images;
package cercles is
type Cercle is new Image with
record
rayon:Float;
end record;
procedure put( p: in Cercle );
end cercles;
52
Le package cercles (2/2)
with Ada.Text_io;use Ada.Text_io;
with Ada.Float_Text_io;use Ada.Float_Text_io;
with Ada.Integer_Text_io;use Ada.Integer_Text_io;
package body cercles is
procedure put( p: in Cercle) is
begin
put( "couleur = " );put( Couleur'image(p.teinte) );
put( ", place = " );put("(");put( p.place.x,3 );
put( "," );put( p.place.y,3 );put(")");
put( ", rayon = " );put( p.rayon );new_line;
end put;
end cercles;
53