Guide Pascal et Delphi
Date de publication : 20/10/07
Par
Frédéric Beaulieu
Cette page recense toute l'aide disponible sur les mini-projets. Pour chaque mini-projet,
il est proposé l'accès à des indications, à la solution guidée pas à pas ainsi qu'au
téléchargement du projet constitué par cette solution.
Si vous constatez une ou plusieurs erreurs dans les solutions proposées, ou si vous
souhaitez que je corrige votre ou vos essais, contactez-moi en joignant le mini-projet
dans son état actuel compressé dans un fichier ZIP. Je ne m'engage à aucun délai, mais
j'essaierai dans la mesure de mes possibilités de répondre aux demandes.
I. Mini-projet n°1 : Nombre secret
I.A. Rappel de l'énoncé
I.B. Indications
I.B.1. Indications générales
I.B.2. Indications avancées
I.C. Solution pas à pas
I.C.1. Création du projet
I.C.2. Création de l'interface
I.C.3. Fonctionnement du bouton « Nouveau nombre secret »
I.C.4. Fonctionnement du bouton « Proposer un nombre »
I.D. Téléchargement
II. Mini-projet n°2 : Première Liste
II.A. Rappel de l'énoncé
II.B. Indications
II.C. Solution pas à pas
II.D. Téléchargement
III. Mini-projet n°3 : Equations du second degré
III.A. Enoncé
IV. Mini-projet n°5 : Tableaux Associatifs
IV.A. Enoncé
IV.B. Etapes de progression
IV.C. Solution pas à pas
IV.D. Téléchargement
I. Mini-projet n°1 : Nombre secret
I.A. Rappel de l'énoncé
Ce mini-projet étant le premier, il est possible que vous ayez du mal à le réaliser.
Dans ce cas, n'hésitez pas dans ce cas à consulter les indications, puis le guide
pas à pas, ou en dernier recours à télécharger le mini-projet terminé (auquel vous
avez accès même si vous avez la version téléchargée du site).
Rappels et indications indispensables :
- Pour pouvoir réagir au clic sur un bouton, il faut faire un
double-clic dessus dans Delphi, puis écrire les instructions dans la procédure
qui est créée à cet effet (Ecrire la procédure et la déclarer sans passer par cette
étape du double-clic ne suffit en aucun cas).
I.B. Indications
I.B.1. Indications générales
I.B.2. Indications avancées
-
Pour obtenir un entier entre 0 et 100, il faut écrire :
Le résultat devra être affecté à la variable destinée à recevoir le
nombre secret.
- Pour demander un nombre à l'utilisateur, deux variables sont nécessaires.
Une, de type chaîne, est d'abord initialisée, puis utilisée dans un appel à
'InputQuery'. La deuxième permettra de stocker le résultat de la conversion de
la première par 'IntToStr'. Tout cela s'effectue en trois instructions.
-
Pour gérer les trois cas (inférieur, supérieur, égal), voici le
squelette du bloc 'if' à utiliser :
if ... < ... then
...
else if ... > ... then
...
else
begin
...
end ;
|
I.C. Solution pas à pas
Cette solution guidée pas à pas vous permettra de construire le projet entier.
Rappelez-vous que la version de Delphi employée ici peut différer de la votre,
mais que cela n'a normalement que des conséquences mineures.
I.C.1. Création du projet
- Sous Delphi, créez un nouveau projet à l'aide de la commande 'Nouveau Projet' ou 'Nouvelle Application' du menu 'Fichier'.
- Enregistrez le projet par la commande 'Enregistrer Tout' du menu Fichier.
Choisissez de préférence un répertoire créé pour cette occasion. Un nom pour le projet
(fichier .dpr) vous sera demandé, entrez « NombreSecret ». Un nom d'unité (.pas)
sera également demandé pour stocker l'unique fiche et son unité associée :
nommez-la « Principale » (et pensez à toujours donner à vos unités des noms
significatifs qui soient également des identificateurs)
I.C.2. Création de l'interface
- Affichez la fiche principale : au besoin, sélectionnez, dans la liste des
unités, l'unité 'Principale' et appuyez sur F12 pour passer à sa fiche
associée.
- Une fois la fiche visible, trouvez, dans la barre des composants, le
bouton 'Button'. Le fait de cliquer sur ce bouton puis sur la fiche place un
'Button' (un bouton, donc), sur la fiche à l'endroit où vous cliquez. Placez
ainsi deux boutons sur la fiche.
- Le moment est venu de placer tout cela : il va falloir utiliser soit
la souris, soit l'inspecteur d'objets. Si vous voulez utiliser la souris,
rappelez-vous que les actions que vons effectuez agissent sur le composant
sélectionné (la fiche et chaque bouton, je le rappelle, sont des composants),
il vous faudra donc par exemple sélectionner un bouton (en cliquant une seule
fois dessus) avant de changer sa position ou ses dimensions à l'aide de la
souris.
- En ce qui concerne l'inspecteur d'objets, affichez-le au besoin à
l'aide de la touche F11. Le tableau suivant donne les propriétés à changer
ainsi que les nouvelles valeurs à donner à ces propriétés ainsi qu'une brève
explication (Vous devez savoir utiliser l'inspecteur d'objets au moins de
manière rudimentaire si vous avez suivi le cours normal du guide).
Fiche :
Caption |
Le Nombre Secret |
Permet de choisir le texte de titre de la fenêtre |
Height |
148 |
Donne la hauteur de la fiche en pixels |
Width |
193 |
Donne la largeur de la fiche en pixels |
Bouton « Nouveau nombre secret »
Caption |
Nouveau nombre secret |
Fixe le texte qui apparait sur le bouton |
Height |
25 |
Hauteur du bouton |
Left |
24 |
Décalage par rapport au bord gauche de la fiche |
Top |
24 |
Décalage par rapport au haut de la fiche |
Width |
137 |
Largeur du bouton |
Bouton « Proposer un nombre »
Caption |
Proposer un nombre |
Fixe le texte qui apparait sur le bouton |
Height |
25 |
Hauteur du bouton |
Left |
24 |
Décalage par rapport au bord gauche de la fiche |
Top |
72 |
Décalage par rapport au haut de la fiche |
Width |
137 |
Largeur du bouton |
I.C.3. Fonctionnement du bouton « Nouveau nombre secret »
Maintenant que l'interface est terminée, concentrons-nous sur le code Pascal.
- Un clic sur le premier bouton doit déclencer une action. Pour cela,
le seul moyen que vous connaissiez consiste à double-cliquer sur le bouton sous
Delphi, puis à écrire des instructions dans la procédure qui est alors générée
(ceci est expliqué en détail dans un chapitre du guide). Double-cliquez donc
sur le bouton « Nouveau nombre secret ». Une procédure est générée, prète à
recevoir nos instructions.
- Le principe ici est de fixer une nouvelle valeur, donc de changer la
valeur d'une variable. Cette variable, nous pourrions la déclarer dans la
procédure qui vient d'être générée, mais alors elle serait non seulement
inaccessible depuis d'autres procédures, mais aussi "oubliée" lorsque la
procédure se termine (elle n'existerait que pendant l'exécution de la procédure).
Il nous faut donc déclarer cette variable dans un endroit plus accessible.
Parmi les possibilités, l'interface ou l'implémentation de l'unité 'Principale',
ou le fichier-projet. L'endroit le plus indiqué est au début de l'implémentation
de l'unité 'Principale' car alors la variable sera accessible à toute procédure
qui suivra. En outre, le fichier-projet est à exclure dans la grande majorité
des cas, et l'interface est inadaptée car la variable n'a pas à être accessible
depuis d'autres unités. Déclarez donc une variable 'NbSecret' de type 'Integer'
au début de l'implémentation de l'unité (c.f. code source à la fin de cette
solution).
-
Quant à la procédure proprement dite, il va suffir de générer un nombre
aléatoire entre 0 et 100, opération que l'on fait en deux temps (mais en
une seule instruction) : on génère un nombre aléatoire entre 0 (inclus) et
101 (exclus), puis ce nombre à virgule est tronqué pour donner un entier
entre 0 et 100. Enfin, le résultat de ces deux opérations est affecté à
'NbSecret'. Voici l'instruction à inscrire :
NbSecret := Trunc(Random(101 ));
|
I.C.4. Fonctionnement du bouton « Proposer un nombre »
Maintenant, il ne nous reste plus qu'à faire fonctionner le deuxième bouton,
c'est un peu plus compliqué.
- Créez la procédure de réponse au clic sur ce bouton, comme pour
le précédent.
-
On va devoir ici dans un premier temps demander un nombre à l'utilisateur.
Il est impossible de lui demander directement un entier à ce stade du guide,
nous devrons donc passer par une chaîne de caractères et une conversion.
Il va donc nous falloir deux variables : une de type chaîne qui stockera la
réponse de l'utilisateur, et une autre de type entier pour stocker la
réponse convertie (ici encore on supposera l'utilisateur assez gentil
pour ne donner que des nombres). Voici la déclaration des deux variables
locales à la procédure :
var
NbTest: Integer ;
Reponse: string ;
|
-
Le questionnement de l'utilisateur passera comme d'habitude par un
appel à 'InputQuery'. Il faudra, comme à chaque fois, lui fournir
une variable dont la valeur sera utilisée comme réponse suggèrée.
La variable 'Reponse' servira à cela et sera initialisée avant
utilisation (il faut toujours initialiser les variables avant utilisation).
Une instruction convertira ensuite la réponse en nombre.
Voici les trois premières instructions de la procédure, qui
effectue les tâches décrites ci-dessus :
Reponse := ' 0 ' ;
InputQuery(' Proposez un nombre ' , ' Valeur : ' , Reponse);
NbTest := StrToInt(Reponse);
|
-
Plusieurs cas sont alors à envisager : soit l'utilisateur à entré un
nombre inférieur ou supérieur au nombre secret, et on l'en informe, soit
il a trouvé le nombre secret et davantage de choses devront être faites.
Voici la structure du bloc 'if' qui examinera les cas :
if NbTest < NbSecret then
ShowMessage(' Le nombre secret est supérieur à ' + IntToStr(NbTest))
else if NbTest > NbSecret then
ShowMessage(' Le nombre secret est inférieur à ' + IntToStr(NbTest))
else
begin
...
end ;
|
Vous noterez que dans les deux premiers cas, une simple instruction
suffit et rappelle à l'utilisateur sa proposition.
-
Le dernier cas nécessite un bloc d'instructions. En effet, il va falloir
féliciter l'utilisateur, fixer un nouveau nombre secret, et en avertir
l'utilisateur. Voici les trois instructions à inscrire dans ce bloc
(vous remarquerez que la deuxième est identique à celle utilisée par
l'autre bouton) :
ShowMessage(' bravo, le nombre secret était ' + IntToStr(NbSecret));
NbSecret := Trunc(Random(101 ));
ShowMessage(' Nombre secret changé, vous pouvez rejouer ' );
|
Le projet est maintenant complet, vous pouvez le compiler et le tester à loisir.
Voici le code source complet de l'implémentation de l'unité 'Principale' :
implementation
var
NbSecret: Integer ;
procedure TForm1.Button1Click(Sender: TObject);
begin
NbSecret := Trunc(Random(101 ));
end ;
procedure TForm1.Button2Click(Sender: TObject);
var
NbTest: Integer ;
Reponse: string ;
begin
Reponse := ' 0 ' ;
InputQuery(' Proposez un nombre ' , ' Valeur : ' , Reponse);
NbTest := StrToInt(Reponse);
if NbTest < NbSecret then
ShowMessage(' Le nombre secret est supérieur à ' + IntToStr(NbTest))
else if NbTest > NbSecret then
ShowMessage(' Le nombre secret est inférieur à ' + IntToStr(NbTest))
else
begin
ShowMessage(' bravo, le nombre secret était ' + IntToStr(NbSecret));
NbSecret := Trunc(Random(101 ));
ShowMessage(' Nombre secret changé, vous pouvez rejouer ' );
end ;
end ;
end .
|
Enfin, en guise de conclusion, voici une liste de choses qui pourraient être
améliorées dans ce projet. Je ne suis pas assez bête pour dire que cette liste
est complète, mais elle donne quelques idées qui feront l'objet de recherches
par les plus courageux.
- Si l'utilisateur entre un nombre qui n'est pas entre 0 et 100, rien
ne lui est reproché, ce serait mieux.
- La limite 0 est peu contestable, par contre, la valeur 100
pourrait être fixée par l'utilisateur au moyen d'une question posée
par un troisième bouton (il faudrait dans ce cas recalculer un nouveau
nombre secret).
- L'instruction qui initialise le nombre secret est répètée deux
fois. Dans ce genre de cas, il est préférable d'en faire une procédure
ou une fonction séparée, qui sera appelée en lieu et place de l'instruction.
- L'utilisateur pourrait avoir un nombre limité d'essais, et
pourrait aussi avoir un score.
I.D. Téléchargement
II. Mini-projet n°2 : Première Liste
II.A. Rappel de l'énoncé
L'objectif de ce mini-projet est de vous faire créer une application exploitant
les notions que vous venez de découvrir, à savoir :
- Gestion de composants, propriétés et méthodes.
- Utilisation d'une zone d'édition, d'une liste.
Le but de ce projet est, à partir de l'interface que voici, composée d'une liste,
d'une zone de saisie et de trois boutons, de permettre les actions mentionnées
ci-dessous :
- Ajout de texte dans la liste : pour cela, on entre le texte dans la zone
d'édition puis on clique sur le bouton. Le texte est alors ajouté à la liste et la
zone de saisie est vidée (le texte y est effacé).
- Effacement de la liste : en cliquant sur le bouton adéquat, le contenu de
la liste sera effacé.
- Fermeture de l'application, en cliquant sur 'Quitter'.
II.B. Indications
- Chaque bouton nécessite une procédure de réponse au clic : une
instruction est suffisante pour chacun des 2 boutons 'Effacer la liste' et
'Quitter'.
- Pour l'ajout, vous devez utilisez la méthode 'Add' de la propriété
'Items' de la liste, en transmettant soit une variable chaîne (ce qui allonge
la procédure mais est plus simple au début), ou directement transmettre le
texte de la zone d'édition.
- Pour effacer le contenu de la zone de saisie, il faut assigner la
chaîne vide à la propriété 'Text' de la zone d'édition.
II.C. Solution pas à pas
Cette solution ne rentrera pas dans les détails de la création de l'interface,
domaine qui doit maintenant vous être familier et qui ne doit pas poser de problème
particulier. Voici les propriétés importantes à fixer pour les composants :
Composant |
Propriété |
Valeur |
Fiche |
Name : |
fmPrinc |
Fiche |
Caption : |
Première Liste |
Liste |
Name : |
lbArchives |
Zone d'édition |
Name : |
edPhrase |
Zone d'édition |
Text : |
(Vide) |
Bouton Ajout |
Name : |
btAjout |
Bouton Ajout |
Caption : |
Ajout |
Bouton Effacer |
Name : |
btEffacer |
Bouton Effacer |
Caption : |
Effacer la liste |
Bouton Quitter |
Name : |
btQuitter |
Bouton Quitter |
Caption : |
Quitter |
Dans la procédure de réponse au clic sur le bouton 'Quitter', on doit faire appel
à la méthode 'Close' de la fiche, soit l'unique instruction suivante :
Dans la procédure de réponse au clic sur le bouton 'Effacer la liste', on
doit faire appel à la méthode 'Clear' de la propriété 'Items' du composant
'lbArchives'. Voici donc l'unique instruction à inscrire :
Dans la procédure de réponse au clic sur le bouton 'Ajouter', vous devez tout d'abord
récupérer la valeur du texte de la zone d'édition, soit 'edPhrase.Text'. Cette valeur
sera ensuite transmise en tant que paramètre à la méthode 'Add' de 'Items' de 'lbArchives'.
Ensuite, il suffit d'affecter la chaîne vide à 'edPhrase.Text'. Voici une première version,
longue, de cette procédure :
procedure TfmPrinc.btAjoutClick(Sender: TObject);
var
Temp: string ;
begin
Temp := edPhrase.Text;
lbArchives.Items.Add(Temp);
edPhrase.Text := ' ' ;
end ;
|
et voici la procédure un peu plus courte qui fait la même chose :
procedure TfmPrinc.btAjoutClick(Sender: TObject);
begin
lbArchives.Items.Add(edPhrase.Text);
edPhrase.Text := ' ' ;
end ;
|
II.D. Téléchargement
III. Mini-projet n°3 : Equations du second degré
III.A. Enoncé
Le sujet, loin d'effrayer les matheux, est : Calcul des racines d'un trinôme du second
degré. Que les non matheux se rassurent, je vais poser plus clairement le problème
et faire tous les rappels mathématiques nécessaires.
Le but de l'exercice est de donner les valeurs de X vérifiant l'équation du second degré :
a × X2 + b × X + c = 0
où a, b et c sont des constantes réelles, a non nul.
Nous nous placerons au niveau de la classe de première. Je rappelle que
l'existence de solutions dépend d'une valeur Delta calculée à partir de a,
b et c. Voici la valeur de Delta :
Delta = b2 - 4 × a × c
Trois cas sont alors possibles suivant le signe de Delta :
Si vous avez suivi la progression normale du guide, vous savez effectuer chacune
des opérations nécessaire au calcul de ces solutions (je rappelle cependant que la
fonction sqrt permet de calculer une racine carrée).
C'est à vous de déterminer quelles variables et quels types vous allez utiliser. Je
ne vous donnerai pas l'interface, mais seulement une idée de ce qu'elle devrait être :
elle montrera en haut de la seule fenêtre du logiciel un polynôme avec 3 zones
d'édition pour remplir les valeurs des coéfficients a, b et c. Vous pourrez supposer
que l'utilisateur donnera des valeurs réelles correctes, pour cette fois. En dessous,
un mémo affichera les résultats des calculs lancés par un bouton : valeur de Delta,
existence de solutions et valeurs éventuelles de ces solutions. Un autre bouton
effacera le contenu des zones d'édition ainsi que du mémo, et un dernier permettra
de quitter le logiciel.
Vous veillerez à certains détails, comme à vérifier que a est bien non nul, sans quoi
vos calculs risquent de ne pas fonctionner. Pensez également à initialiser vos
variables, et à vider le mémo avant d'y écrire du texte. Vous êtes libre quant aux
noms donnés à vos composants et quant aux textes affichés. Veillez cependant à créer
une interface agréable que vous apprécieriez vous-même.
En option (pour ceux qui connaissent uniquement) : L'équation admet des solutions non
réelles si Delta est négatif. Donnez les solutions dans ce cas.
IV. Mini-projet n°5 : Tableaux Associatifs
IV.A. Enoncé
Certains langages de programmation comme PHP proposent un type de données
particulièrement intéressant qui n'a aucun équivalent en Pascal Objet. Ce genre
de tableau utilise en fait n'importe quel indice pour contenir n'importe quelle
donnée. Ainsi, on peut trouver dans un tableau associatif une chaîne indexée par
un entier et un enregistrement indexé par une chaîne. La taille de ce type de
tableau est en outre variable, et dépend des éléments ajoutés et retirés du
tableau associatif.
Ce mini-projet consiste à réaliser une classe permettant l'utilisation d'un
tableau associatif simple indexé uniquement par des chaînes de caractères et
dont chaque élément est également une chaîne de caractères. Pour ce qui est du
stockage d'un nombre quelconque d'éléments, vous avez le droit d'utiliser la
classe "TList" qui permet de sauvegarder un nombre variable de pointeurs. L'accès
aux éléments du tableau associatif doit se faire via une propriété tableau par
défaut, pour un maximum de simplicité. Ainsi, les instructions suivantes, où "TA"
est un tableau associatif, se doivent de fonctionner :
TA[' prénom ' ] := ' Jacques ' ;
TA[' nom ' ] := ' Dupont ' ;
TA[' age ' ] := ' 53 ' ;
ShowMessage(TA[' prénom ' ] + ' ' + TA[' nom ' ] + ' a ' + TA[' age ' ] + ' ans. ' );
|
IV.B. Etapes de progression
- Comme dans tout projet donnant lieu à la création de classes, commencez
par réfléchir : quels sont vos objectifs, vos contraintes en termes de programmation,
en termes de connaissances ? Quelle est votre marge de manoeuvre ?
- Créez un projet vierge et une unité sans fiche. Dans cette dernière,
déclarez la classe "TableauAsssociatif" avec deux sections privées et publiques.
- Documentez-vous un peu sur TList : regardez l'aide de Delphi sur "TList",
sur son constructeur (Create), son destructeur (Destroy), les méthodes "add",
"remove" et "pack".
- Déclarez un champ (privé) de classe TList. Surchargez le constructeur
et le destructeur pour créer et détruire cet objet à la volée.
- Un élément sera stocké sous la forme d'un pointeur vers un enregistrement.
Déclarez donc un enregistrement et le type pointeur correspondant. L'enregistrement
doit contenir deux chaînes : "cle" et "valeur".
- Commencez par coder les méthodes les plus simples, celles ne nécessitant pas
trop de recherche. Ainsi, il est impératif de pouvoir connaître la taille d'un tableau
associatif (son nombre d'éléments). Créez donc une propriété "Count" en lecture seule.
Utilisez en lecture une méthode faisant appel à la méthode de même nom de l'instance
de "TList".
- Les méthodes suivantes sont plus complexes, puisqu'à une clé ne peut
correspondre qu'une seule valeur. Il va nous falloir un moyen de savoir si une clé
donnée existe déjà dans le tableau. Créez pour cela une méthode privée acceptant une
chaîne en paramètre ("cle") et retournant un pointeur vers un élément du type
enregistrement créé auparavant. Cette méthode recherchera dans la liste un élément
comportant comme clé la chaîne transmise. Si cet élément est trouvé, il est retourné
comme résultat. Dans le cas contraire, la méthode retournera nil.
- Créez maintenant une méthode d'ajout d'élément à deux paramètres de type
chaîne ("cle" et "valeur"). Cette méthode recherche d'abord dans la liste fElems si
la clé existe déjà (utilisez la méthode créée juste avant). Dans l'affirmative, sa
valeur associée est remplacée par la "valeur" transmise. Dans la négative, un nouvel
élément est créé et placé dans la liste interne "fElems".
- Le programmeur doit avoir un moyen simple d'accèder à la valeur associée à
une clé. Créez donc une méthode obtenirValeur qui à partir d'un paramètre "cle" de
type chaîne fourni un résultat de type chaîne. Si la clé existe, la valeur est
retournée, sinon on prend comme convention la chaîne vide.
- A l'aide des deux méthodes d'ajout (AjoutElement) et de recherche de valeur
(obtenirValeur), vous pouvez maintenant déclarer une propriété tableau de type chaîne
indexée par une chaîne (l'aviez-vous vue venir ? Si oui bravo, si non remarquez l'ordre
dans lequel nous avons procédé : les deux méthodes puis la propriété). Les deux
accesseurs sont évidemment les deux méthodes citées précédemment.
- Il manque encore la possibilité de supprimer une entrée en spécifiant sa
clé. Créez donc une méthode publique "retirerElement" prenant un paramètre chaîne et
tentant de retirer l'élément comportant cette chaîne comme clé. Aucune erreur ne
sera signalée en cas d'absence de l'élément dans le tableau. Vous pouvez utiliser
la méthode "extract" de la classe "TList" pour vous aider.
- Il manque une dernière chose pour que la classe soit acceptable : la
libération de la mémoire associée aux pointeurs non retirés du tableau lors de sa
destruction. En effet, cette mémoire n'est pour l'instant libérée nulle part. Le
meilleur endroit pour réaliser cela est lors de la destruction, donc lors de l'appel
au destructeur "Destroy". Complètez donc le destructeur surchargé en y incorporant
la libération de mémoire.
- La toute dernière étape consiste à vérifier que l'extrait de code fourni
fonctionne.
IV.C. Solution pas à pas
La classe que nous créons ici va servir de "boite noire" permettant de créer un
tableau associatif restreint. L'intérêt de créer une classe est qu'on pourra en
déclarer autant d'instance que nous voulons. Ces instances seront indépendantes
les unes des autres. Le fait d'avoir des éléments publics et privée ainsi qu'une
propriété tableau par défaut donne à la fois accès uniquement aux membres importants
de la classe et facilite leur accès.
Cette solution pas à pas reprend les intitulés précédent en les complétant autant
que possible pour donner tout le cheminement qui amène à la résolution complète du
mini-projet :
- Comme dans tout projet donnant lieu à la création de classes, commencez
par réfléchir : quels sont vos objectifs, vos contraintes en termes de programmation,
en termes de connaissances ? Quelle est votre marge de manoeuvre ?
L'objectif (mais pas forcément sa réalisation, ce qui n'a rien à voir) est ici
très simple : créer une classe implémentant des fonctionnalités. Les contraintes
de programmation sont nombreuses : programmer sous Delphi, en utilisant la
programmation objet, utiliser une propriété tableau par défaut, utiliser la classe
"TList" (sachez lire entre les lignes : ce qui est "possible" est la plupart du temps
conseillé et ici un impératif. Vous devrez sans cela réaliser vous-même un TAD liste
permettant de stocker les éléments du tableau). Les contraintes en termes de
connaissances sont ici le nerf de la guerre car c'est justement pour acquérir des
connaissances que vous réalisez ce mini-projet. Vous devrez donc porter toute votre
attention sur l'utilisation scrupuleuse des notions concernant la programmation objet.
L'utilisation de la classe "TList" est un passage presque incontournable, mais pas
l'essentiel : gardez en tête que l'utilisation des objets a été vue dans un
chapitre antérieur.
-
Créez un projet vierge et une unité sans fiche. Dans cette dernière, déclarez
la classe "TableauAsssociatif" avec deux sections privées et publiques.
Il s'agit ici de créer la base de travail sous Delphi. Le projet vous servira
à tester directement la classe à créer, et l'unité vierge permet d'écrire du
code indépendant de toute fiche, ce qui est impératif puisque nous devons créer
une classe et non une application. Voici le code source de l'unité destinée à
contenir la classe terminée :
unit tabassoc;
interface
type
TableauAssociatif = class
private
public
end ;
implementation
end .
|
- Documentez-vous un peu sur TList : regardez l'aide de Delphi sur "TList",
sur son constructeur (Create), son destructeur (Destroy), les méthodes "add",
"remove" et "pack".
Si ca n'a pas été fait auparavant (et c'est une erreur
fréquente de débutant que de se lancer dans l'aventure sans emmener sa bible, à
savoir la documentation de Delphi), il est impératif de regarder comment fonctionne
la classe "TList". Elle manipuile des pointeurs génériques (Pointer), ce qui va nous
donner l'occasion de quelques transtypages.
On ajoute un élément via la méthode "Add", on en retire via "Remove". Il est précisé
dans l'aide de Delphi que ces deux méthodes ne gèrent pas la méthode associée aux
pointeurs : ce sera à vous de le faire.
-
Déclarez un champ (privé) de classe TList. Surchargez le constructeur et
le destructeur pour créer et détruire cet objet à la volée.
Comme le tableau associatif va stocker ses éléments dans un objet de classe TList,
et qu'on ne peut pas utiliser de variable globale ou locale (où pourrions-nous les
placer ???), il reste le choix idéal du champ. Ainsi, chaque objet de classe
"TableauAssociatif" aura sa propre instance de la classe "TList" dans laquelle
les éléments du tableau seront stockés. Voici la déclaration des trois nouveaux
membres de "TableauAssociatif" :
TableauAssociatif = class
private
fElems: TList;
public
constructor Create; virtual ;
destructor Destroy; override ;
end ;
|
et voici le code source du constructeur et
du destructeur. Notez l'emploi de "Free" :
constructor TableauAssociatif.Create;
begin
fElems := TList.Create;
end ;
destructor TableauAssociatif.Destroy;
begin
fElems.Free;
inherited ;
end ;
|
-
Un élément sera stocké sous la forme d'un pointeur vers un enregistrement.
Déclarez donc un enregistrement et le type pointeur correspondant.
L'enregistrement doit contenir deux chaînes : "cle" et "valeur".
Rien de compliqué ici. Le type enregistrement et son pointeur associé vont
être les éléments stockés dans l'instance de "TList". C'est une chose à faire
avant de commencer à coder les différentes méthodes d'ajout, de retrait, de
recherche... Placez simplement les deux déclarations de type avant la
déclaration de la classe :
type
TElementTableauAssociatif = record
Cle: string ;
Valeur: string ;
end ;
PElementTableauAssociatif = ^TElementTableauAssociatif;
|
-
Commencez par coder les méthodes les plus simples, celles ne nécessitant pas
trop de recherche. Ainsi, il est impératif de pouvoir connaître la taille
d'un tableau associatif (son nombre d'éléments). Créez donc
une propriété "Count" en lecture seule. Utilisez en lecture une méthode
faisant appel à la méthode de même nom de l'instance de "TList".
Il s'agit ici d'une mise en bouche. Voici la déclaration de cette propriété
très simple :
property Count: integer
read getCount;
|
déclarez la méthode "getCount" comme suit :
....
fElems: TList;
function getCount: integer ;
public
...
|
Voici le code source de cette méthode :
function TableauAssociatif.getCount: integer ;
begin
Result := fElems.Count;
end ;
|
-
Les méthodes suivantes sont plus complexes, puisqu'à une clé ne peut
correspondre qu'une seule valeur. Il va nous falloir un moyen de savoir
si une clé donnée existe déjà dans le tableau. Créez pour cela une méthode
privée acceptant une chaîne en paramètre ("cle") et retournant un pointeur
vers un élément du type enregistrement créé auparavant. Cette méthode
recherchera dans la liste un élément comportant comme clé la chaîne
transmise. Si cet élément est trouvé, il est retourné comme résultat.
Dans le cas contraire, la méthode retournera nil.
Cette méthode va nous rendre beaucoup de services puisque c'est elle qui
permettra d'atteindre des éléments déjà placés dans le tableau (mais en
interne uniquement : le programmeur utilisant la classe terminée ne devra
pas avoir connaissance de cette méthode). Il s'agit ici d'un bête parcours
de la liste, avec à chaque fois un transtypage de pointeur puis une
comparaison de chaîne. Voici le code source ; étudiez-le à fond :
function TableauAssociatif.rechercheElem(Cle: string ): PElementTableauAssociatif;
var
indx: integer ;
begin
indx := 0 ;
Result := nil ;
while (indx < fElems.Count) and (Result = nil ) do
begin
if PElementTableauAssociatif(fElems.Items[indx])^.Cle = Cle then
Result := fElems[indx];
inc(indx);
end ;
end ;
|
N'oubliez pas de déclarer cette méthode dans la section privée de la classe.
-
Créez maintenant une méthode d'ajout d'élément à deux paramètres de type
chaîne ("cle" et "valeur"). Cette méthode recherche d'abord dans la liste
fElems si la clé existe déjà (utilisez la méthode créée juste avant). Dans
l'affirmative, sa valeur associée est remplacée par la "valeur" transmise.
Dans la négative, un nouvel élément est créé et placé dans la liste interne
"fElems".
On rentre ici dans le vif du sujet. Voici la déclaration de la méthode,
qui doit être privée (nous verrons plus tard pourquoi) :
...
function rechercheElem(Cle: string ): PElementTableauAssociatif;
procedure AjoutElement(Cle: string ; Valeur: string );
public
...
|
La méthode marche en deux temps : le premier recherche une entrée
correspondante à la clé. Si cette entrée existe, la valeur est
simplement mise à jour dans un second temps. Sinon, on doit réaliser un
ajout complet. Voici le code source :
procedure TableauAssociatif.AjoutElement(Cle, Valeur: string );
var
Elem: PElementTableauAssociatif;
begin
Elem := rechercheElem(Cle);
if Elem <> nil then
Elem^.Valeur := Valeur
else
begin
new(Elem);
Elem^.Cle := Cle;
Elem^.Valeur := Valeur;
fElems.Add(Elem);
end ;
end ;
|
-
Le programmeur doit avoir un moyen simple d'accèder à la valeur associée à une
clé. Créez donc une méthode obtenirValeur qui à partir d'un paramètre "cle" de
type chaîne fourni un résultat de type chaîne. Si la clé existe, la valeur est
retournée, sinon on prend comme convention la chaîne vide.
Cette méthode est très simple : elle fait appel à la recherche d'un élément
avec la clé transmise. Si l'élément est trouvé, sa partie valeur est retournée,
sinon, c'est une chaîne vide. Voici le code source :
function TableauAssociatif.obtenirValeur(Cle: string ): string ;
var
Elem: PElementTableauAssociatif;
begin
Elem := rechercheElem(Cle);
if Elem <> nil then
Result := Elem^.Valeur
else
Result := ' ' ;
end ;
|
N'oubliez pas encore une fois de déclarer cette méthode dans la section privée
de la classe.
-
A l'aide des deux méthodes d'ajout (AjoutElement) et de recherche de valeur
(obtenirValeur), vous pouvez maintenant déclarer une propriété tableau de type
chaîne indexée par une chaîne (l'aviez-vous vue venir ? Si oui bravo, si non
remarquez l'ordre dans lequel nous avons procédé : les deux méthodes puis la
propriété). Les deux accesseurs sont évidemment les deux méthodes citées
précédemment.
Nous avons créé auparavant exactement ce qu'il faut pour faire fonctionner une
propriété tableau de type chaîne indexée par une chaîne. En effet, nous avons,
pour la lecture, une fonction à un seul paramètre chaîne qui retourne une chaîne,
et pour l'écriture une procédure à deux paramètres de type chaîne. Reste à deviner
la fonction de la propriété : c'est tout simplement une propriété "Valeur" (som
nom est sans importance) auquel on accède en donnant une chaîne servant de clé
entre crochets. Le nom est bien sans importance puisque la propriété va être
déclarée "par défaut". Voici la déclaration proprement dite (il n'y a besoin d'aucun
code source supplémentaire !) :
...
property Count: integer
read getCount;
property Valeur[cle: string ]: string
read obtenirValeur
write AjoutElement; default ;
end ;
...
|
-
Il manque encore la possibilité de supprimer une entrée en spécifiant sa clé.
Créez donc une méthode publique "retirerElement" prenant un paramètre chaîne
et tentant de retirer l'élément comportant cette chaîne comme clé. Aucune
erreur ne sera signalée en cas d'absence de l'élément dans le tableau. Vous
pouvez utiliser la méthode "extract" de la classe "TList" pour vous aider.
Pouvoir ajouter des éléments de manière transparente est bien joli, mais il
peut être intéressant d'en retirer de manière explicite cette fois. Cette
méthode le fera, sans toutefois râler si la clé transmise n'est pas présente
dans le tableau. La méthode extract de TList permet d'extraire sans douleur
un pointeur d'une liste, il faut donc connaître la valeur du pointeur avant
l'appel de cette méthode, et libèrer la mémoire associée à ce pointeur après
l'appel, car l'élement doit être supprimé. Voici le code source :
procedure TableauAssociatif.RetirerElement(Cle: string );
var
Elem: PElementTableauAssociatif;
begin
Elem := rechercheElem(Cle);
if Elem <> nil then
begin
fElems.Extract(Elem);
Dispose(Elem);
end ;
end ;
|
-
Il manque une dernière chose pour que la classe soit acceptable : la
libération de la mémoire associée aux pointeurs non retirés du tableau lors
de sa destruction. En effet, cette mémoire n'est pour l'instant libérée nulle
part. Le meilleur endroit pour réaliser cela est lors de la destruction, donc
lors de l'appel au destructeur "Destroy". Complètez donc le destructeur
surchargé en y incorporant la libération de mémoire.
La libération en elle-même se fait facilement puisqu'il suffit d'un parcours
de la liste et d'un "Dispose" sur chaque pointeur transtypé au préalable
(appeler dispose sur un pointeur de type Pointer n'a aucun sens).
destructor TableauAssociatif.Destroy;
var
indx: integer ;
begin
for indx := 0 to fElems.Count - 1 do
Dispose(PElementTableauAssociatif(fElems[indx]));
fElems.Free;
inherited ;
end ;
|
-
La toute dernière étape consiste à vérifier que l'extrait de code fourni
fonctionne.
Pour cela, placez un simple bouton sur l'unique fiche de l'application.
Utilisez le code ci-dessous, qui reprend le code fourni, pour complèter
la méthode de réponse au clic sur le bouton :
procedure TForm1.Button1Click(Sender: TObject);
var
TA: TableauAssociatif;
begin
TA := TableauAssociatif.Create;
TA[' prénom ' ] := ' Jacques ' ;
TA[' nom ' ] := ' Dupont ' ;
TA[' age ' ] := ' 53 ' ;
ShowMessage(TA[' prénom ' ] + ' ' + TA[' nom ' ] + ' a ' + TA[' age ' ] + ' ans. ' );
TA.Destroy;
end ;
|
Le résultat obtenu lors d'un clic confirme le bon fonctionnement du tableau :
Libre à vous de créer un exemple plus complet, ou d'utiliser cette classe dans vos
projets. Ce mini-projet est maintenant terminé.
IV.D. Téléchargement
Copyright © 2000 Frédéric Beaulieu.
Aucune reproduction, même partielle, ne peut être faite
de ce site ni de l'ensemble de son contenu : textes, documents, images, etc.
sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à
trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.