Un tableau de procédures ? Comment et pourquoi ?
L'utilisation de l'instruction
case ... of en Delphi, quoique pratique dans la plupart des cas de programmation peut parfois se révéler inappropriée lorsque le nombre de branchements devient trop grand :
- Lourdeur du code
- Vitesse d'exécution désastreuse.
Ce cas peut parfois être rencontré lorsque l'on écrit par exemple :
- un émulateur : on effectue une action particulière différente pour chaque instruction du microprocesseur. Dans ce cas, la vitesse d'exécution est primordiale.
- un jeu d'aventure :
- Chaque case d'une carte peut être un type de rencontre différente
- Chaque objet possédé par le héros possède un effet différent lorsqu'il est utilisé, chaque arme une attaque particulière, chaque sortilège déclenche un effet particulier
Dans ce cas c'est la clarté du code qui permettra le design d'un univers riche en contenu et rebondissements.
Par exemple, ce type de code...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| procedure TForm1.UneActionADeterminer(Parametre:integer);
begin
...
case Parametre of
0:MaProcedure0;
1:MaProcedure1;
2:MaProcedure2;
3:MaProcedure3;
4:MaProcedure4;
5:MaProcedure5;
.....
203:MaProcedure203;
//etc...
end;
...
end; |
...gagnera en clarté et en célérité si nous pouvions le transformer en :
1 2 3 4 5 6
| procedure TForm1.UneActionADeterminer(Parametre:integer);
begin
...
MesProcedures[Parametre];
...
end; |
Nous venons de présenter ici le cas d'un tableau de procédures sans paramètre. Nous allons voir qu'il est tout aussi possible d'utiliser un tableau de fonctions ou bien encore d'utiliser des paramètres.
Pour aboutir à ce résultat, nous devons :
- Définir un type de variable pouvant contenir un type générique de procédure ou de fonction avec ou sans paramètre (Variables procédurales) :
Il s'agit de définir ici un type de variable un peu particulière destiné à recevoir une procédure ou une fonction. Ce genre de variable s'appelle une variable procédurale. La syntaxe pour déclarer ce type de variable est la suivante :
<Nom du Type> =
<procedure ou function>[(
<paramètres>)[:
<type de resultat>]] [
of object;]
(Nota : "of object" signifie que nos procédures ou fonctions seront définies dans une classe comme TForm1 par exemple : elles font partie d'un objet.)
Ainsi aurons nous ce genre de déclarations pour des :[list:787f454628] - Variables de type procedure :[list:787f454628]
- avec paramètres (c'est ce genre de déclaration qui est utilisé pour définir des variables de type évènement comme TNotifyEvent, TDrawItemEvent, etc..) :
1 2
| type
TProcedureAvecParametres=procedure(a,b:integer;s:string) of object; |
- sans paramètres :
1 2
| type
TProcedureSimple=procedure of object; |
Variables de type
function :
- avec paramètres :
1 2
| type
TOperationArithmetique=function(a,b:real):real of object; |
- sans paramètres :
1 2
| type
TTraiterUnCodeErreur=function:boolean of object; |
Ecrire nos procédures ou fonctions :
L'exemple suivant concerne des fonctions avec paramètres, définies dans une classe (notez le
"of object")
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| type
TOperationArithmetique=function(a,b:real):real of object;
TForm1 = class(TForm)
...
private
{ Déclarations privées }
public
{ Déclarations publiques }
...
TableauDeFonctions:array[0..3] of TOperationArithmetique;
function Addition(a,b:real):real;
function Multiplication(a,b:real):real;
function Division(a,b:real):real;
function Soustraction(a,b:real):real;
...
end;
...
implementation
...
function TForm1.Addition(a,b:real):real;
begin
result:=a+b;
end;
function TForm1.Multiplication(a,b:real):real;
begin
result:=a*b;
end; |
Les fonctions sont des fonctions globales n'appartenant pas à une classe (disparition du
"of object" :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| type
TOperationArithmetique=function(a,b:real):real;
TForm1 = class(TForm)
...
private
{ Déclarations privées }
public
{ Déclarations publiques }
...
TableauDeFonctions:array[0..3] of TOperationArithmetique;
...
end;
function Addition(a,b:real):real;
function Multiplication(a,b:real):real;
function Division(a,b:real):real;
function Soustraction(a,b:real):real;
...
implementation
...
function Addition(a,b:real):real;
begin
result:=a+b;
end;
function Multiplication(a,b:real):real;
begin
result:=a*b;
end;
... |
Assigner une procédure à chaque élément du tableau :
Dans cette partie de code, nous allons simplement remplir notre tableau aussi simplement que nous le ferions avec des variables simples comme des entiers ou des chaînes de caractères :
1 2 3 4 5 6 7
| procedure TForm1.InitialisationDuTableau
begin
TableauDeFonctions[0]:=Addition;
TableauDeFonctions[1]:=Multiplication;
...
//etc...
end; |
Utiliser notre tableau :
- Avec des procédures sans paramètres :
1 2 3 4 5 6 7 8 9
| procedure TForm1.UneActionADeterminer(Parametre:integer);
begin
//si Parametre est compris entre la borne inférieure et la borne supérieure du tableau de procédures
if ((Parametre>=low(TableauDeProcedures)) and (Parametre<=high(TableauDeProcedures)) then
//si l'entrée du tableau contient effectivement quelque chose (une procédure)
if Assigned(TableauDeProcedures[Parametre]) then
//exécution de la procédure ad hoc
TableauDeProcedures[Parametre];
end; |
- Avec des fonctions acceptant des paramètres :
1 2 3 4 5 6
| procedure TForm1.Calcule(OperationChoisie:integer;NombreA,NombreB:real;var resultat:real);
begin
if ((OperationChoisie>=0) and (OperationChoisie<=3)) then
if Assigned(TableauDeFonctions[OperationChoisie]) then
resultat:=TableauDeFonctions[OperationChoisie](NombreA,NombreB);
end; |
Un exemple complet :
Dans un nouveau projet, nous avons placé un
TButton sur notre fiche.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
//Définition d'un type de procedure simple
TProcedureAffichage=procedure of object;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Déclarations privées }
public
{ Déclarations publiques }
TableauDeProcedures:array[0..2] of TProcedureAffichage;
procedure AffichageShowMessage;
procedure AffichageDansLaFiche;
procedure AffichageBarreDeTitre;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.AffichageShowMessage;
begin
showmessage('Procedure n°1');
end;
procedure TForm1.AffichageDansLaFiche;
begin
canvas.font.Color:=random($ffffff);
canvas.TextOut(10,10,'Procédure n°2');
end;
procedure TForm1.AffichageBarreDeTitre;
var i:integer;
oldcaption:string;
begin
oldcaption:=Caption;
for i:=0 to 9 do
begin
caption:='Procedure n°3';
sleep(200);
caption:='';
sleep(100);
end;
caption:=oldcaption;
end;
//Evènement OnClick du Bouton
procedure TForm1.Button1Click(Sender: TObject);
var i:integer;
begin
//choix aléatoire d'une procedure d'affichage
i:=Random(3);
if assigned(TableauDeProcedures[i]) then
TableauDeProcedures[i];
end;
//Evènement OnCreate de la fiche
procedure TForm1.FormCreate(Sender: TObject);
begin
Randomize;
TableauDeProcedures[0]:=AffichageShowMessage;
TableauDeProcedures[1]:=AffichageDansLaFiche;
TableauDeProcedures[2]:=AffichageBarreDeTitre;
end;
end. |
Précision sur les variables procédurales : pointeurs ou variables ?
C'est un grand débat qui dépasse le cadre et l'objectif de cette simple FAQ. En effet, il est tout de même étonnant de constater que bien que ce type se gère comme une variable classique, c'est un type de variable à laquelle s'applique le
@, le
nil et le
Assigned()des pointeurs. Si vous souhaitez approfondir le sujet, nous vous suggérons la lecture de la documentation de Delphi aux rubriques suivantes :
- Assigned,fonction
- Types procédure
- Types procédure dans les instructions et les expressions
Faites vos jeux !
Partager