Des Records aux Classes
Date de publication : 18/09/2007 , Date de mise à jour : 18/09/2007
Par
Eric Leconte (Clorish) (ericleconte.developpez.com)
Cet articles a pour but d'aborder en douceur la création et la manipulation de Classes.
Au travers d'un exemple simple : La gestion d'un agenda, nous verrons comment convertir un programme
a base de Record en application orientée objet, manipulant des Classes.
I. Introduction
Les Objets (ou Classes) sont souvent considérés par les debutants comme hors de porté de leurs
conaissances techniques, preferant à ces dernieres une solution d'aparence plus simple :
les Enregistrement (ou Records). Cela est généralement basé sur une méconnaissance des classes.
Le but de ce tutoriel est donc de montrer qu'une classe peut etre tres simple à manipuler.
Afin d'etudier les differences entre Classes et Records, nous allons nous appuyer sur un contexte
applicatif simple : Un agenda electronique.
Apres une rapide presentation de l'application basé sur un systeme de donnés géré par des Records, on
glissera progressivement vers une structure fondé sur les Classes.
L'application ne sera pas etudié en integralitée afin de nous concentrer sur la gestion des données en mémoire.
Simplifié au maximum, notre agenda sera composé d'un ensemble de fiches (Nom, Prenom, Adresse, Numero(s) de
telephone, ...) et de quelques fonctions de traitement sur ces fiches. Certaines libertés seront parfois
necessaires afin d'illustrer au mieux certains point techniques.
Remarque : Tout au long de cet article, nous allons utiliser le type Record des versions antérieures
Delphi 7 et inferieur. Pour des raisons pedagogiques, nous ne tiendrons pas compte des evolution du langages apparues
dans les versions Borland Studio 2005 et plus.
II. Conception de l'application de base
II-A. Les déclarations
Notre agenda se repose sur 2 type de variables :
- Les Fiches.
- L'agenda.
Les fiches contiennent les informations relative a une persone : Nom, Prénom, Adresse et Numero(s) de téléphone.
L'agenda lui est constitué d'un ensemble de fiches.
Type
TFiche = Record
Nom : String ;
Prenom : String ;
Adresse : String ;
CodePostal : String ;
Ville : String ;
Telephone : String ;
End ;
TAgenda = Array of TFiche;
|
II-B. Traitements sur les fiches
Notre application doit effectuer certains traitement de formatage des données contenue dans les fiches :
- Le nom doit etre en majuscule : DUPOND.
- Le prénom doit etre en minuscule, avec la première lettre en majuscule : Alain.
- Le nom complet est composé du nom, suivi du prénom : DUPOND Alain.
- La ville doit etre en majuscule : MARSEILLE.
- L'adresse complete est formatée de la manière suivante :
NOM Prenom
Adresse
CodePostal VILLE
Procedure FormatNom(VAR fiche : TFiche);
Begin
Fiche.Nom := UpperCase(Fiche.Nom);
End ;
Procedure FormatPrenom(Var Fiche : TFiche);
Begin
Fiche.Prenom := LowerCase(Fiche.Prenom);
Fiche.Prenom[1 ] := UpperCase(Fiche.Prenom[1 ];
End ;
Procedure FormatVille(Var Fiche : TFiche);
Fiche.Ville := UpperCase(Ville);
End ;
Function NomComplet(Fiche : TFiche) : String ;
Begin
Result := Fiche.Nom + ' ' + Fiche.Prenom;
End ;
Function AdresseComplete(Fiche : TFiche) : String ;
Begin
Result := NomComplet + # 13 # 10 ;
Result := Result + Fiche.Adresse + # 13 # 10 ;
Result := Result + Fiche.CodePostal + ' ' + Fiche.Ville;
End ;
|
II-C. Traitements sur l'agenda
Notre application effectue egalement destraitements sur l'agenda :
- Ajout d'une fiche.
- Suppression d'une fiche.
- Creation d'une fiche.
Procedure AjouterFiche(Fiche : TFiche);
Var Size : Integer ;
Begin
Size := Length(Agenda);
SetLength(Agenda, Size+1 );
Agenda[Size] := Fiche;
End ;
Procedure SupprimerFiche(Index : Integer );
Var Size : Integer ;
Begin
Size := Length(Agenda);
Agenda[Index ] := Agenda[Size-1 ];
SetLength(Agenda, Size-1 ];
end ;
Function CreerFiche(Nom, PRenom, Adresse, CodePostal, Ville, Telephone) : TFiche;
Begin
Result.nom := Nom;
Result.Prenom := Prenom;
Result.Adresse := Adresse;
Result.CodePostal := CodePostal;
Result.Ville := Ville;
Result.Telephone := Telephone;
FormatNom(Result);
FormatPrenom(Result);
FormatVille(Result);
End ;
|
II-D. Optimisation du code
Dans le code presenté au dessus, on peut remarquer plusieurs choses qui peuvent etre optimisé :
- Le passage du parametre "fiche" par adresse (VAR).
- Le besoin recurent d'un parametre "fiche".
Il est necessaire de transmettre la fiche par adresse dans les fonctions de modification sous peine
de perdre les modification effectuer. Ce que l'on peut faire c'est manipuler les fiches via des Pointeurs.
Quand au parametre recurent, on peut se servir d'une variable globale que l'on affecterais avant les differents
appels aux methodes de traitement.
II-D-1. Gestion des fiches par Pointeurs
Type
PFiche = ^Fiche;
TFiche = Record
End ;
TAgenda = Array of PFiche;
Procedure FormatNom(fiche : PFiche);
Begin
Fiche^.Nom := UpperCase(Fiche^.Nom);
End ;
Procedure SupprimerFiche(Index : Integer );
Var Size : Integer ;
Begin
Size := Length(Agenda);
Dispose(Agenda[Index ]);
Agenda[Index ] := Agenda[Size-1 ];
SetLength(Agenda, Size-1 ];
end ;
Function CreerFiche(Nom, PRenom, Adresse, CodePostal, Ville, Telephone) : PFiche;
Begin
New(Result);
Result^.nom := Nom;
Result^.Prenom := Prenom;
FormatNom(Result);
FormatPrenom(Result);
FormatVille(Result);
End ;
|
Remarque : On notera l'usage de 2 fonctions supplementaire : New et Dispose
responsable de l'allocation memoire d'une fiche et de sa liberation. On en considère plus
les fiches comme des variables complexes, mais comme des poiteurs vers une zone memoire qui contient
les données.
II-D-2. Simplification du parametre recurent "fiche"
Var Selected : PFiche;
Procedure FormatNom;
Begin
Selected^.Nom := UpperCase(Selected^.Nom);
End ;
Function CreerFiche(Nom, PRenom, Adresse, CodePostal, Ville, Telephone) : PFiche;
Begin
New(Result);
Result^.nom := Nom;
Result^.Prenom := Prenom;
Selected := Result;
FormatNom;
FormatPrenom;
FormatVille;
End ;
|
III. Migration de l'application vers une structure "Objet"
III-A. Les déclarations
Convertir une application basé sur un type Record vers un type Class est tres simple :
Type
PFiche = ^TFiche;
TFiche = Record
Nom : String ;
Prenom : String ;
Adresse : String ;
CodePostal : String ;
Ville : String ;
Telephone : String ;
End ;
TAgenda = Array of PFiche;
Var Selected : PFiche;
|
Type
TFiche = Class
Nom : String ;
Prenom : String ;
Adresse : String ;
CodePostal : String ;
Ville : String ;
Telephone : String ;
End ;
TAgenda = Array of TFiche;
Var Selected : TFiche;
|
En fait le code diffère juste par l'emploi du mot clef Class en lieu et place du mot clef Record.
On notera la simplification du code qui ne necessite plus de type "Pointeur". En effet, les objets (variables de
type Class) sont deja des pointeur vers une structure mémoire contenant les donnée de l'objet.
III-B. Manipulation des Objets
Si on considère un objet comme une variable de type Pointeur sur un Record, le code précédent reste
quasiement inchangé. Par contre le côté Pointeur implicite des objets permet encore une fois d'alleger
le code.
Procedure FormatNom;
Begin
Selected.Nom := UpperCase(Selected.Nom);
End ;
Procedure SupprimerFiche(Index : Integer );
Var Size : Integer ;
Begin
Size := Length(Agenda);
Agenda[Index ].Destroy;
Agenda[Index ] := Agenda[Size-1 ];
SetLength(Agenda, Size-1 ];
end ;
Function CreerFiche(Nom, PRenom, Adresse, CodePostal, Ville, Telephone) : PFiche;
Begin
Result := TFiche.Create;
Result.nom := Nom;
Result.Prenom := Prenom;
Selected := Result;
FormatNom;
FormatPrenom;
FormatVille;
End ;
|
On notera l'absence du caractere "^" devenu implicite, et un systeme de creation/Liberation de la mémoire
légèrement different :
New(Variable) deviens Variable := Type.Create;
Dispose(Variable) deviens Variable.Destroy;
Les methodes Create sont dite Constructeur car elles construisent les objets.
Les methodes Destroy sont dites Destructeur car elles detruisent les objets.
III-C. Optimisation des procedures et fonctions
Maintenant que notre application manipule des objets à la place d'enregistrements, il nous
reste plus qu'a voir comment optimiser les appels aux procédures et fonctions.
On s'est rendu compte dans les paragraphes precedents que ces fonctions sont toutes liés a des variables
ou plutot des types :
- FormatNom : TFiche
- FormatPrenom : TFiche
- FormatVille : TFiche
- NomComplet : TFiche
- AdresseComplete : TFiche
- AjouterFiche : TAgenda
- SupprimerFiche : TAgenda
- CreerFiche : TAgenda
En effet, chacune de ces fonctions necessitent : Soit un parametre récurent de meme type, Soit s'appuyent
sur une meme variable globale.
Dans ce cas, il semblerais logique de regroupper et d'associer ces fonction a nos données. Ces fonctions
vont donc devenir des methodes :
Type
TFiche = Class
Nom : String ;
Prenom : String ;
Adresse : String ;
CodePostal : String ;
Ville : String ;
Telephone : String ;
Procedure FormatNom;
Procedure FormatPrenom;
Procedure FormatVille;
Function NomComplet : String ;
Function AdresseComplete : String ;
End ;
Procedure TFiche.FormatNom;
Begin
End ;
|
Pour devenir une méthode, une fonction a tout juste besoin d'être declarée dans notre classe
(anciennement Record) et d'etre précédé du nom de la classe en question lors de son
implementation. De cette manière, on peux declarer plusieurs fonctions de même non, dans des classes
differentes sans soucis de collision.
L'appel à ces methodes se fera donc au travers d'une instance :
Var Fiche : TFiche
Fiche.FormatNom;
|
III-D. Référence à l'instance d'une classe
Le fait que ces méthodes nécéssite une instance pour etre appelée,
Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur.
La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.