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.