IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Indentation et conseils d'écriture en pascal.

DELPHI = PASCAL.

Petit rappel : DELPHI est un outil de développement pour le langage PASCAL pour WINDOWS. Son origine est le TURBO-PASCAL développé par le français Philippe KHAN.

Quand vous développez un programme vous devez toujours garder à l'esprit qu'un tiers, pourra être amener à le modifier ou à le compléter. Un des objectifs des préconisations d'écriture est de faciliter la compréhension d'un code source. La maintenance corrective et évolutive sera ainsi réduite en évitant de fastidieuses phases de reprise de code. En résumé, facilitez la compréhension de ce que fait le code plutôt que comment il le fait. Je montre ici, simplement ma façon de faire, je ne peux que conseiller (en particulier les débutants) de s'en inspirer.

Je tiens à remercier l'équipe DELPHI de Developpez.com, et en particulier Laurent Dardenne pour son aide et pour ses conseils.

Article lu   fois.

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

1.   Préambule.

Je suis accoutumé à relire et à corriger du code, à l'enseigner dans notre équipe de travail et à reprendre des sources fournis par des prestataires de service, souvent bourrés d'erreurs et manquant de sécurités, alors qu'ils devraient justement s'appliquer pour que leurs clients puissent reprendre le code avec le moins de peine possible, donc avec moins de perte de temps.

Je me limiterais ici à donner quelques conseils d'écriture, dont je me suis fait des règles.

Voici ce qui représente un véritable cauchemar pour ceux qui doivent reprendre du code pour le corriger, le compléter ou tout simplement l'analyser pour apprendre. Inutile de préciser que si vous imposez cette façon d'écrire à un collègue, vous serez maudit et risquez fort de vous faire injurier. J'ai vu (et corrigé) des codes écrits de cette façon sur plus de 400 lignes.

 
Sélectionnez
procedure TBDlisteImage.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); 
var
n:integer;
begin
inherited mousedown(button,shift,x,y);
n:=x div wfixe;
n:=n + (colcount * ( y div hfixe));
if button=mbLeft then
begin 
if (n<>indexcourant) and ((indexbase+n)<=listephoto.Count-1) then
setindexcourant(n)
else
if ((indexbase+n)<=listephoto.Count-1) then
affiche(self);
end
else 
if (button=mbRight) and ((indexbase+n)<=listephoto.Count 1) then
begin 
imprime_image;
end;
end;

Ceci montre un exemple court, qui n'est pas de moi. Je me suis trouvé devant le cas suivant : un composant graphique qui correspondait à ce que je cherchais pour une application. Malheureusement, il y avait encore des Bogues et il manquait des sécurités. J'ai voulu reprendre le code pour l'analyser et le corriger. Je me suis trouvé devant un truc inextricable que j'ai commencé à ré indenter. Mais c'était tellement énorme, que j'ai renoncé. Dommage pour celui qui a écrit le composant et à qui on dit qu'il a travaillé comme un cochon. Non ? Bien sur, nous avons tous nos règles et petites habitudes qui nous conviennent. Cependant, nous devons toujours garder à l'esprit que nos codes seront utilisés par un tiers, soit parce qu'il est libre de droit, soit par nécessité de modification ou de débogage.

Commençons par corriger l'indentation de cette procédure .

 
Sélectionnez
procedure TBDlisteImage.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); 
var
  n : integer; 
begin
   inherited mousedown(button,shift,x,y);
   n := x div wfixe;
   n := n + (colcount * ( y div hfixe));
   if button = mbLeft then
    begin 
       if (n<>indexcourant) and ((indexbase+n)<=listephoto.Count-1) then
       setindexcourant(n)
       else
         if ((indexbase+n)<=listephoto.Count-1) then affiche(self);
    end
   else 
     if (button=mbRight) and ((indexbase+n)<=listephoto.Count-1) then
   begin 
      imprime_image;
   end;
end;


C'est tout de même plus lisible, non ? Voici un autre exemple pour terminer ce préambule.

 
Sélectionnez
function TBDlisteimage.dessinerimage;
var
st:string;
h,l,r:double; 
h1,l1:integer; 
img:Tpicture; 
begin
img:=Tpicture.Create;
st:=listephoto[indexbase+index]; 
result:=true; 
try
try 
img.LoadFromFile(st);
except
on EInvalidGraphic do
begin
listephoto.Delete(indexbase+index);
result:=false; 
exit; 
end;
end;
h:=img.graphic.Height; 
l:=img.graphic.Width; 
h1:=hfixe-(marge*2); 
l1:=wfixe-(marge*2); 
try
if ((l/h)<=(l1/h1)) then
begin
r:=l/h*h1;
l1:=trunc(r); 
end
else
begin
r:=h/l*l1;
h1:=trunc(r); 
end;
except
begin
listephoto.Delete(indexbase+index);
result:=false; 
exit; 
end;
end;
canvas.Brush.color:=self.color; canvas.Pen.Color:=clwhite; 
canvas.Pen.Width:=1; 
canvas.Rectangle( ((index mod colcount) * wfixe) + self.Left + 2,
((index div colcount) * hfixe) + self.top+2,
((index mod colcount) * wfixe)+wfixe+self.Left-1,((index div colcount)
* hfixe)+hfixe+self.top-1); 
canvas.StretchDraw(rect((wfixe-l1) div 2+ (index mod colcount *
wfixe),(hfixe-h1) div 2+ (index div colcount * hfixe),
(wfixe-l1) div 2+ (index mod colcount * wfixe)+l1,(hfixe-h1) div 2+ 
(index div colcount * hfixe)+h1),img.graphic);
finally
img.free;
end;
end;


C'est à se demander comment l'auteur a réussi à se relire. Visiblement pas sensibilisé au fait qu'un tiers voudra lire son code pour quelque raison que se soit.

2.   Base de l'indentation.

L'indentation dans l'écriture de code n'a aucun effet sur son exécution. Son intérêt est de le rendre lisible, d'en faciliter l'analyse et la correction. Il n'y a pas vraiment de règles, mais par expérience je sais qu'il n'y a pas 36 façons d'indenter correctement du code. Tous les programmeurs confirmés utilisent presque les mêmes méthodes. Cependant il est clair que par nécessité nous changeons quelques fois notre façon de faire. En terme simple l'indentation consiste à organiser le texte représentant le code de façon à le rendre (visuellement) aussi lisible que possible. Cela consiste principalement à décaler les lignes de texte comme je l'ai montré en préambule.

3.   Comment nommer une procédure, une fonction, un objet ?

Choisissez des noms clairs et significatifs. Cela évitera souvent des commentaires et vous permettra de mieux vous souvenir de l'élément (fonction, procédure...) que vous voulez utiliser. Donnez lui le nom de ce qu'elle est sensée faire.

Exemple : Si c'est une fonction chargée de vérifier si une chaîne de caractères représente un nombre entier :

 
Sélectionnez
Function ChaineEntierValide(Chn : String) : Boolean

et non pas :

 
Sélectionnez
Function CEV(S : String) : Boolean;

Il est clair que si une fonction dont le nom n'est pas parlant mais documentée, cela ne parait pas important. Mais si elle s'appelle "ChaineEntierValide" et qu'elle fait partie d'une bibliothèque d'outils, je m'en souviens parce qu'elle porte le nom de ce qu'elle est sensée faire. C'est une vieille habitude qui m'est bien profitable. Borland a déclaré la procédure "SetLength" et non pas "SL", c'est la même règle.
Si vous avez besoin de trois tableaux devant contenir l'un des noms, l'autre des valeurs numériques et le troisième des adresses.

Vous pouvez les déclarer comme ceci :

 
Sélectionnez
Tableau1 : Array Of String; 
Tableau2 : Array[0..200] Of Integer; 
Tableau3 : Array Of String;


Alors dès le début, écrivez une documentation pour vous aider.
Faite comme ceci :

 
Sélectionnez
TableauNoms,
TableauAdresses : Array Of String; 
TableauValeurs : Array[0..200] Of Integer;


Encore une fois, cet exemple court ne parait pas pertinent, mais imaginez ce que cela peut donner dans une application conséquente.

Très important, donnez des noms significatifs aux boutons et autres objets. Ce n'est pas indispensable si ils sont peu nombreux, mais vous serez vite perdu dans une grosse application.

 
Sélectionnez
Procedure TForm1.BitBtn1Click(Sender : TObject); 
Procedure TForm1.Button1Click(Sender : TObject);


Voila qui facilitera votre travail .

 
Sélectionnez
Procedure TForm1.BtOuvrirClick(Sender : TObject); 
Procedure TForm1.BtCalculClick(Sender : TObject);


C'est la même chose pour les noms des fiches.
Plutôt que Form1, Form2 ... Form10, choisissez des noms clairs. FichePrincipale, FicheConfiguration, FicheImpression ... Là encore, en écrivant le programme, vous vous souviendrez immédiatement du nom de la fiche que vous voulez appeler.

4.   Déclarations de variables types et constantes.


Il est courant de voir ceci .

 
Sélectionnez
Type
TabInfoTube=Array[0..49] Of Array[1..8] Of ShortString; 
Const
CheminResultats:String='c:\resdo'; 
CycleEnCours:Boolean=False; 
SaisiesValides:Boolean=False; 
ParametresValides:Boolean=False; 
NbTubes:Integer=1; 
SourceValide:Boolean=False; 
TensionDeBase:Double=8; 
ReferenceTubeZero:Boolean=True; 
SELDIRHELP = 1000; 
Var
ErreurCoef:Boolean;
FichePrincipale:TFichePrincipale; 
Densite,PoidsSec:Double; 
TempsEcouleInt:Int64; 
Lecture:Integer; 
Abandon:Boolean; 
Debut:TDateTime; 
FichierSource: String; 
InfoTube:TabInfoTube; 
// Support EXCEL 
OleApplication:Variant; 
OleWorkBook:Variant; 
implementation


Mais ça c'est mieux parce que clair et lisible .

 
Sélectionnez
Type
  TabInfoTube = Array[0..49] Of Array[1..8] Of ShortString; 
  
Const
   CheminResultats   : String = 'c:\resdo'; 
   CycleEnCours      : Boolean = False; 
   SaisiesValides    : Boolean = False; 
   ParametresValides : Boolean = False; 
   NbTubes           : Integer = 1; 
   SourceValide      : Boolean = False; 
   TensionDeBase     : Double = 8; 
   ReferenceTubeZero : Boolean = True; 
   SELDIRHELP = 1000;
   
Var
  ErreurCoef         : Boolean;
  FichePrincipale    : TFichePrincipale; 
  Densite, PoidsSec  : Double; 
  TempsEcouleInt     : Int64; 
  Lecture            : Integer; 
  Abandon            : Boolean; 
  Debut              : TDateTime; 
  FichierSource      : String; 
  InfoTube           : TabInfoTube; 
  // Support EXCEL
  OleApplication     : Variant; 
  OleWorkBook        : Variant;
   
implementation


Même si cela parait plus esthétique que nécessaire, ce n'est pas une perte de temps que de réaligner si on change ou ajoute une variable (ce qui n'est pas obligatoire). Je reconnais qu'un alignement parfait n'est pas indispensable. Mais au moins un espace de chaque coté de ":" Valeur : Word; Je préconise cette méthode, car cela me facilite beaucoup mon travail de reprise de code et je vais beaucoup plus vite car c'est beaucoup plus lisible.

5.   Indentations de blocs.


Le rôle d'un bloc est de cloisonner les parties de code dépendant d'une condition et/ou de regrouper des instructions liées à un même traitement.
Voici les deux façons les plus courantes d'indenter des blocs "begin end".

 
Sélectionnez
If Valeur1 = Valeur2 Then begin
//Traitement
end;


Je préfère la seconde, parce que beaucoup plus lisible.

 
Sélectionnez
If Valeur1 = Valeur2 Then 
begin
   //Traitement
end;


Un cas particulier, "Repeat Until". Il constituent ensemble un bloc. Donc :

 
Sélectionnez
Repeat
   begin
      // Traitement
   end;
Until Fin;


"begin et end" sont inutiles.

Ne décalez pas trop et pas systématiquement les blocs d'instructions. Un décalage trop important dans de longue procédures ou fonctions, va élargir le texte au point de sortir de l'écran. Cela rendra la lecture difficile. Là encore, ce n'est pas une règle mais un conseil.
Pour moi un scrollling latéral est plutôt gênant.

6.   Les blocs "begin end" et les sauts de lignes.


J'ai l'habitude de considérer que ce qui n'est pas nécessaire est inutile.
Je vais montrer deux exemples simples de l'écriture d'un test conditionnel .

 
Sélectionnez
If Valeur1 = Valeur2 Then 
 begin 
    MessageDlg('Réussi',mtInformation,[mbOK],0);
 end;


Pourquoi faire simple quand on peut faire compliqué ? Ici, le bloc "begin end" est parfaitement inutile. Si ils manquent, et que l'on a besoin d'ajouter des instructions suite au test conditionnel, il est tout de même facile de les rajouter.

 
Sélectionnez
If Valeur1 = Valeur2 Then MessageDlg('Réussi',mtInformation,[mbOK],0);


Les sauts de lignes entre plusieurs blocs d'instructions. Pas indispensable mais quelques fois bien commodes.

 
Sélectionnez
{--------------------------------------------------------------------- }
procedure TFichePrincipale.FormActivate(Sender: TObject); 
Var
  W,Y,C : Integer; 
  
begin
   If DebutProgramme Then 
    begin 
       DebutProgramme := False;
       Image1.Width := 800; 
       Image1.Height := 600; 
       //      
       //
       With Image1.Picture.Bitmap Do 
        begin 
           Width := 800;
           Height := 600; 
           Canvas.Brush.Color := clWhite; 
           Canvas.Pen.Color := clBlack; 
           Canvas.Rectangle(0,0,799,599); 
        end; 
       //    
       // 
       W := Image3.Width;
       With Image3.Picture.BitMap Do 
        begin 
           Width := Image3.Width;
           Height := Image3.Height; 
           Canvas.Rectangle(0,0,Image3.Width,Image3.Height); 
        end; 
       //    
       //
       For Y := 255 DownTo 0 Do 
        begin 
           C := 255 - Y;
           Pixel[1] := C; 
           Pixel[2] := C; 
           Pixel[3] := C; 
           Move(Pixel,C,3); 
           With Image3.Picture.BitMap Do 
            begin 
               Canvas.Pen.Color := C;
               Canvas.MoveTo(0,Y); 
               Canvas.LineTo(W,Y); 
            end; 
        end;
       //       
       //
       Image3.Refresh;
       Image4.Picture.BitMap := Image3.Picture.BitMap; 
       TransfertCopiePlan(Image1.Picture.Bitmap); 
       TransfertBitmap(1); 
       If EvaluerDecoloration Then RadioButton2.Checked := True 
       Else RadioButton1.Checked := True; 
       Application.ProcessMessages;
    end; 
   //
   EditCodeBarre.SetFocus;
end ;

7.   L'instruction "with", profitez en.


L'instruction With peut être traduite en français par : "avec" ou "concernant". Il n'y a pas d'équivalent en C ou C++. Cette une instruction très commode qui permet d'alléger le code de façon importante lors de paramétrage de valeurs dans un objet ou une structure. En effet cette instruction ne s'applique qu'à ces derniers.

Exemple 1 concernant un TStringGrid .

 
Sélectionnez
procedure TFicheSaisies.FormCreate(Sender: TObject); 
begin
   StringGrid1.Cells[0,0] := 'Tube';
   StringGrid1.Cells[1,0] := 'Organisme'; 
   StringGrid1.Cells[2,0] := 'Milieu'; 
   StringGrid1.Cells[3,0] := 'Produit'; 
   StringGrid1.Cells[4,0] := 'Dose'; 
   StringGrid1.Cells[5,0] := 'Témoins'; 
   StringGrid1.Cells[6,0] := 'Observations'; 
   Coll:=1;
   Lig:=1; 
end ;


Plus simple :

 
Sélectionnez
procedure TFicheSaisies.FormCreate(Sender: TObject); 
begin
   With StringGrid1 Do 
    begin 
       Cells[0,0] := 'Tube';
       Cells[1,0] := 'Organisme'; 
       Cells[2,0] := 'Milieu'; 
       Cells[3,0] := 'Produit'; 
       Cells[4,0] := 'Dose'; 
       Cells[5,0] := 'Témoins'; 
       Cells[6,0] := 'Observations'; 
    end; 
   Coll:=1;
   Lig:=1; 
end ;


Exemple 2 concernant une structure.

 
Sélectionnez
Type
  TabOrgMil = Record 
                 NomOrganisme  : String[20];
                 TypeOrganisme : Integer; 
                 CodeOrganisme : Word; 
              end; 
              
Var
  Organismes : TabOrgMil; 
  
Procedure Init; 
begin
   With Organismes Do 
    begin 
       NomOrganisme := 'Botritice';
       TypeOrganisme := 1; 
       CodeOrganisme := 125; 
    end; 
end ;

8.   Les lignes de séparation, indispensables.


Séparez toutes vos procédures et fonctions par une ligne de commentaire neutre. Je sais bien que pour beaucoup cela n'est pas nécessaire, mais après de nombreuses années d'expérience, je peux dire que c'est indispensable.

Procédez comme suit.

 
Sélectionnez
Var
  Configuration : TConfiguration; 
//------------------------------------------------------------
Function TFicheLecture.Etat : Integer; 
Begin
   Result := EtatControle;
End; 
//------------------------------------------------------------
Procedure TFicheLecture.DemandeAutorisation; 
Begin
   Clignotant1.Enabled := True;
   BtPret.Enabled := False; 
   ComPort1.WriteStr('PORTE' + #13); 
   BtDemande.Enabled := False; 
End; 
//------------------------------------------------------------
Procedure TFicheLecture.Fairelecture; 
Begin
   BtPret.Enabled := False;
   Clignotant1.Enabled := True; 
   ComPort1.WriteStr('LECTURE'+ #13); 
End; 
//------------------------------------------------------------
Procedure TFicheLecture.ConfigureLePort; 
Begin
   With Configuration Do


Bien que plus esthétique que réellement utile, cela facilite beaucoup la lecture .

9.   Les commentaires : Pour quoi faire, comment et où les placer ?

Je rassure tout de suite ceux qui pensent qu'ils vont augmenter la taille du fichier une fois compilé. Cela m'a déjà été donné comme argument pour s'en passer. Les commentaires ne sont pas intégrés dans le code, et sont ignorés du compilateur.

Un commentaire en terme de programmation a avant tout un sens explicatif. Il peut représenter une note, un aide mémoire, une courte (mais claire) explication sur ce que fait la fonction ou la procédure, comment est utilisée une variable et même un petit exemple d'utilisation de la routine.
Il faut se contenter du nécessaire, et de le décrire de façon claire et aussi simple que possible, sans en faire trop. On pourrait mettre un commentaire à chaque ligne de code. Mais comme un exemple vaut mieux qu'un long discours, voici.

Les commentaires en entête de fonctions.

 
Sélectionnez
{--------------------------------------------------------}
{ Ajoute un antislash (\) au chemin si il manque         } 
{--------------------------------------------------------} 
Function CompleteChemin(Chemin : String) : String; 
Begin
   If Chemin[Length(chemin)] <> '\' Then Chemin := Chemin + '\';
End; 
{------------------------------------------------------- }
{ Compte le nombre de mots d'une phrase en utilisant     } 
{ un ou plusieurs caractères séparateurs de champs       } 
{ Exemple : Mots:=CompteMots(Chaine,[',',';',#9]);       } 
{ Utilisation de la virgule, du point virgule            } 
{ et du tabulateur comme séparateur de champs            } 
{--------------------------------------------------------} 
Function CompteMots(S : String; WordDelims : EnsembleCarac) : Integer; 
var
  I, Count : Integer; 
  
begin
   If Length(S) = 0 Then Result := 0 
   Else
     begin
        Count := 1;
        For I:=1 To Length(S) Do
         If (S[I] in WordDelims) Then Inc(Count);
        Result := Count;
     end;
end;


Les commentaires dans le code .

 
Sélectionnez
If M > C Then 
 begin 
    C := M;
    // Attribuer autant de colonnes que de mots par ligne
    // La colonne zéro est réservée pour l'indexage. 
    If Grille.ColCount < C + 1 Then Grille.ColCount := C + 1; 
 end; 
Grille.Cells[0,L] := IntToStr(L); 
For I:=1 To M Do Grille.Cells[I,L] := ExtraitMot(I,Chn,Separateur);


Un type particulier de commentaire.

 
Sélectionnez
{-------------------------------------------------------------------}
{                   Faire le négatif d'une image                    }
{-------------------------------------------------------------------}
Procedure Negatif(X1,Y1,X2,Y2 : Integer);
Var
  X,Y : Integer;
  
Begin
   For X:=X1 to X2 Do
    For Y:=Y1 to Y2 Do    
     begin             { Méthode plus rapide }
        PlanImage1[Y,X][1]:=PlanImage1[Y,X][1] XOR $FF;
        PlanImage1[Y,X][2]:=PlanImage1[Y,X][2] XOR $FF;
        PlanImage1[Y,X][3]:=PlanImage1[Y,X][3] XOR $FF;
        {
        Pixel:=PlanImage1[Y,X];
        Pixel[1]:=255 - Pixel[1];
        Pixel[2]:=255 - Pixel[2];
        Pixel[3]:=255 - Pixel[3];
        PlanImage1[Y,X]:=Pixel;
        }
     end;
   TransfertBitMap(1);
End;


Ceci montre que j'ai écrit la partie de code en commentaire en premier. La jugeant trop lente j'ai cherché un algorithme plus rapide. Je laisse l'ancien code le considérant comme aide mémoire.

10. Les tests conditionnels booléen : Ne programmez pas comme un débutant.


Nous avons tous commencé à écrire un test conditionnel sur une variable booléenne de cette façon :

 
Sélectionnez
If Termine = True Then


J'ai appris de cette façon jusqu'au jour où j'ai compris que en quelque sorte on vient d'écrire : "si vérité est vraie alors..." Le test conditionnel est implicite et ne peut prendre que deux valeurs, vrai ou faux.

Donc écrivez très simplement :

 
Sélectionnez
If Termine Then


Ou bien :

 
Sélectionnez
If Not Termine Then


Là non plus, cela paraît pas très pertinent et de toute façon n'est pas obligatoire. Simple habitude d'une écriture plus simple.

Un autre exemple :

 
Sélectionnez
If testVitesse = True AND testVolumeB1 = True AND testVolumeB2 = False AND
   testVolumeB3 = True AND testSommeVolumes = True AND 
   testFlacon = False AND TestFichierPara = False AND testNbflacon = True
 Then ValidationPara := true else ValidationPara := false;


Ouf !

Pourquoi pas comme ceci ?

 
Sélectionnez
If testVitesse AND testVolumeB1 AND (Not testVolumeB2) AND
   testVolumeB3 AND testSommeVolumes AND testFlacon AND
   (Not TestFichierPara) AND testNbflacon Then ValidationPara := true
Else ValidationPara := false;


Et même comme cela !

 
Sélectionnez
ValidationPara := testVitesse AND testVolumeB1 AND (Not testVolumeB2) AND
                  testVolumeB3 AND testSommeVolumes AND testFlacon AND
                  (Not TestFichierPara) AND testNbflacon;


A vous de choisir.

11. Quand et où faut il déclarer une variable, globale, locale... ?


Comme je le disais plus haut, tout ce qui n'est pas nécessaire est inutile. Le langage PASCAL présente un très gros avantage sur le C, c'est de rassembler dans la même unité, l'interface et l'implémentation. Pour ceux qui ne connaissent pas le langage C, l'équivalent de la partie interface en PASCAL est placé dans un autre fichier portant l'extension H.

Déclarez les types, variables et constantes globales à l'unité dans la partie "Implementation". Préférez déclarer dans la partie "interface", les types, variables et constantes, seulement ceux et celles dont d'autres unités auront besoin.

Utilisez les variables locales au maximum, pourquoi ?

Quand j'ai commencé à programmer en TURBO-PASCAL , je l'ai appris avec un autodidacte (comme beaucoup à l'époque), qui avait l'habitude de rassembler toutes les variables dans une même unité appelée UVar. Cela semblait pratique. Jusqu'au jour où nous nous sommes lancé dans la conception d'un robot d'analyse et de recherche de traces (dans un laboratoire de chimie.). Cette machine utilisait six ordinateurs dont un maître qui devait gérer l'ensemble, avec tous les échanges de données par liaison RS232. Nous-nous sommes trouvé devant des centaines de pages de code et une gigantesque unité UVar.
Très vite nous avons été dépassé par les confusions et les interactions de variables. J'ai vite compris que notre façon de faire était à revoir complètement. Je me suis imposé une règle, proscrire toute unité rassemblant des type de données et variables communes à l'ensemble de l'application, sauf absolue nécessité de rendre communs certains types, variables et constantes.

Si dans une procédure ou une fonction vous devez utiliser des variables uniquement dans cette fonction, déclarez les de façon local.
Vous éviterez ainsi de nombreuses confusions et risque de confondre des données dans d'autres fonctions (ou procédures).

Et comme un exemple vaut mieux qu'un long discours…

 
Sélectionnez
unit Fiche1;
 
interface 

uses
  Windows,SysUtils, Messages, Classes, Graphics, Controls, 
  Forms, Dialogs, Menus;
   
type
  TFichePrincipale = class(TForm) 
    Label1: TLabel; 
    Image1: TImage; 
    Image2: TImage; 
    procedure Quitter1Click(Sender: TObject); 
  private
    { Déclarations private } 
  public
    { Déclarations public } 
    Function ChargeDonneesEXCEL : Boolean; 
  end;
   
// Les types, constantes et variables déclarés dans cette partie
// Interface, sont de genre "global". C'est à dire reconnus 
// par toutes les unités faisant appel à celle ci, et par toutes 
// les fonctions et procédures de cette unité.
 
Type
    TabInfoTube    = Array[0..49] OfArray[1..8] Of ShortString;
	 
Const
   CheminResultats     : String = 'c:\resdo';
   CycleEnCours        : Boolean = False; 
   SaisiesValides      : Boolean = False; 
   ParametresValides   : Boolean = False;
    
var
  FichePrincipale      : TFichePrincipale; 
  ErreurCoef           : Boolean; 
  Densite, PoidsSec    : Double; 
  TempsEcouleInt       : Int64; 
  Lecture              : Integer; 
  Abandon              : Boolean; 
  Debut                : TDateTime; 
  FichierSource        : String;
  InfoTube             : TabInfoTube;
   
implementation 
{$R *.DFM}
 
// Les types, constantes et variables déclarés dans cette partie
// Implementation, sont de genre à la fois "global" et "local". 
// C'est à dire "local" à l'unité, mais "global" pour toutes les 
// fonctions et procédure déclarées dans la partie Implementation. 

Type
   TabCoef        = Array[1..7] Of Double;
   TabCorrecteur  = Array[1..2] Of Double;
    
Const
   Chercher       : Boolean = False; 
   Tube1Trouve    : Boolean = False; 
   LectureEnCours : Boolean = False;
    
Var
   TC                    : TabCorrecteur; 
   TableCoef             : TabCoef; 
   LecturesReste, 
   Vite, 
   TopFiche , LeftFiche  : Integer; 
   CheminCoefDO          : String;
   NumTube               : Byte; 
   
//----------------------------------------------------
//-- Vérifier la validité d'une chaîne de caractères 
//---------------------------------------------------- 
Function ChaineValide(S : String) : Boolean;
Var
   I  : Integer; 
   // La variable "I" est strictement locale à la fonction.
   // Je n'ai pas donné de nom explicite à cette variable, 
   // car le code est suffisamment court et compréhensible.
     
Begin
   Result := False; 
   If Length(S) = 0 Then Exit;
   For I := 1 To Length(S) Do
    begin
       If Ord(S[I]) > 32 Then
        begin
           Result := True; 
           Break; 
        end;
    end;
End;
//----------------------------------------------------
//-- Sauvegarder les valeurs de calcul 
//---------------------------------------------------- 
Procedure SauveCoefPoidsSec;
Var
  Fichier : File Of TabCoef;
  Erreur  : Integer;
   
// Les deux variables "Fichier" et "Erreur" sont strictement
// locales à la procédure. Aucune autre fonction ou procédure 
// de l'application ne peut y avoir accès.  
// De plus, j'ai devant les yeux les noms des variables, ce 
// qui me facilite la tâche.

Begin
   TableCoef[5] := TC[1]; 
   TableCoef[6] := TC[2]; 
   If FileExists(CheminCoefDO) Then DeleteFile(CheminCoefDO);
   AssignFile(Fichier,CheminCoefDO); 
   ReWrite(Fichier); 
   Erreur:=IOresult; 
   If Erreur = 0 Then Write(Fichier,TableCoef);
   Erreur:=IOresult; 
End; 

end.

12. Notation hongroise (lien)

Notation hongroise   WikipediA - Hungarian notation.

13. Outils de reformatage et autres liens

Les meilleurs composants VCL et outils pour Delphi et C++Builder Sélectionnés par DEVELOPPEZ.COM

Jedi Code Format Pour DELPHI.

DelForExp Pour DELPHI et KYLIX.

14. L'article au format PDF

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Copyright © 2006 Philippe GORMAND. 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.