2 votes

Séparation des classes d'interface et d'implémentation dans Delphi ?

Je sépare mon code Delphi en unités d'interface et d'implémentation, par exemple.

EmployeeIntf.pas ressemble à ceci

type
 // forward declaration
  TScheduleList = class;
  TDeparment = class;
 TEmployee = class(BDObject)
  ....
    function GetSchedules: TScheduleList;
    function GetDepartment: TDepartment;
 end;

 TEmployeeList = class(DBList)
  ....
 end;

 TEmployeeDM = class(BDDBobject)
  ...
 end;

Ensuite, j'ai les deux unités ScheduleIntf.pas & DépartementIntf.pas qui déclarent la classe TScheduleList et la classe TDepartment.

Ensuite, dans mon unité principale qui combine toutes les unités, cela ressemble à ceci,

Unit BusinessDomain
Interface
uses
 classes
  {$I Interface\EmployeeIntf.pas}
  {$I Interface\DepartmentIntf.pas}
  {$I Interface\ScheduleIntf.pas}
Implementation
uses
 SysUtils
 {$I Implementation\EmployeeImpl.pas}
 {$I Implementation\DepartmentImpl.pas}
 {$I Implementation\ScheduleImpl.pas}
Initialization
finalization

end.

Lorsque je compile ce fichier, le compilateur génère une erreur ;

*Type TScheduleList is not yet completely defined*

Comment puis-je avoir ces classes séparées dans chaque fichier d'unité (.pas) et ensuite faire des déclarations en avant sans que le compilateur jette cette erreur ?

Les classes elles-mêmes sont énormes et je préfère les séparer de cette façon.

Gath

15voto

Uli Gerhardt Points 9321

Mon premier conseil : Sautez complètement cette histoire de $Include. Comme Uwe l'a écrit, trouvez une solution plus proche de Delphi.

Si vous voulez vraiment veulent pour rester dans le style $Include : L'erreur que vous citez se produit parce que les déclarations prospectives ne fonctionnent pas dans les blocs "type". Vous déclarez TScheduleList dans un bloc mais le définissez dans un autre bloc. Pour remédier à ce problème, omettez le mot clé "type" dans vos *Intf.pas et insérez-le dans BusinessDomain.pas avant les includes.

12voto

Uwe Raabe Points 21302

Je crains qu'il n'y ait pas de possibilité de diviser une déclaration de classe en plusieurs fichiers. Si les classes sont si volumineuses, vous devriez envisager une refonte.

Les interfaces constituent une autre alternative :

type
  IEmployee = interface
    { public properties and methods of an employee }
    ...
  end;

type
  TEmployee = class(BDObject, IEmployee)
    ...
  end;

La déclaration de l'interface et de la classe peuvent maintenant résider dans des fichiers différents.

7voto

Tim Jarvis Points 12024

Ulrich a tout à fait raison (+1), même si vous pouvez manipuler vos fichiers d'inclusion pour travailler de cette façon, ce ne sera pas une très bonne expérience de développement pour vous.

Pensez à la directive $Include comme un simple mécanisme de remplacement de texte, les choses qui font de vos unités, eh bien... des unités sont les mécanismes de scoping (sections uses, sections types etc.) ils seront beaucoup plus difficiles à utiliser dans un fichier include, c'est pourquoi les fichiers include sont généralement appelés xxx.inc et non xxxx.pas comme ils ne se tiendront souvent pas comme un fichier source par eux-mêmes. Cela les rend bien sûr très difficiles à utiliser dans un environnement de développement car ils ne sont alors que des fichiers texte et non des unités déboguables.

2voto

skamradt Points 13049

Les fichiers d'inclusion sont l'une de ces anciennes fonctionnalités de Pascal qui continuent de progresser. Il fut un temps où nous n'avions pas de clause uses, et c'était le seul moyen de gérer plusieurs fichiers (bien sûr, nous avons tous programmé en utilisant les commandes wordstar pour la navigation dans le code... attendez, elles existent toujours aussi).

Aujourd'hui, l'utilisation la plus courante des fichiers d'inclusion est d'inclure un bloc de code qui doit être partagé entre plusieurs fichiers et ne peut pas être juste utilisé à partir d'une autre unité. Comme Mason l'a souligné dans un autre commentaire, il s'agirait des blocs IFDEF-DEFINE pour déterminer des choses telles que les options de compilation à activer et les définitions à activer à l'échelle du projet. Nous ne sommes plus liés par la limite de 64k sur un fichier source.

Quelques autres points à considérer. La plupart des outils de recherche de votre source ne peuvent pas naviguer dans vos fichiers include. Il peut être difficile d'utiliser un outil aussi simple qu'une recherche de texte pour trouver le message texte qui n'arrête pas de s'afficher. Il serait préférable de ne pas y mettre de code du tout. Lors de la traduction d'un fichier map en code, je crois que les numéros de ligne spécifiés dans le fichier map correspondraient au fichier total si le fichier include était fusionné en place. Si vous utilisez un outil automatisé tel que MadExcept, la ligne d'erreur signalée peut ne pas correspondre à l'emplacement réel.

Ma suggestion serait d'utiliser les interfaces comme Uwe l'a suggéré. Elles ne sont pas uniquement destinées à COM et peuvent répondre à votre souhait de séparer l'"interface" de l'"implémentation".

1voto

onemach Points 151

Mon commentaire précédent était peut-être un peu présomptueux... En relisant votre question, je pense que vous avez mal compris comment utiliser les unités et en particulier la directive "uses".

Vous pouvez déclarer des classes individuelles à la fois l'interface et l'implémentation dans un seul fichier unitaire :

unit EmployeeDBCLassesU

uses system, DB, Blah, blah; // Units needed by this unit

interface

type

 TEmployeeList = class(DBList)
   Procedure DoSomething;
 end;

 TEmployeeDM = class(BDDBobject)
  Procedure DoSomething;
 end;

implementation

{TEmployeeList}

Procedure TEmployeeList.DoSomething;
begin
...
end;

{TEmployeeDM }

Procedure TEmployeeDM.DoSomething;
begin
...
end;

Pour ensuite les utiliser ailleurs :

Unit BusinessDomain

interface

uses EmployeeDBCLassesU; // MY units needed by this unit
.
.
.

Ceci amène toutes les définitions de classe dans le BusinessDomain.

et vous pouvez alors faire

 TBusinessDomain = class(BDDBobject)
   EmployeeList: TEmployeeList;
   EmployeeDM: TEmployeeDM;
   .
   .
   .;
 end;

J'espère que cela vous aidera davantage, car vous gagnerez beaucoup en adoptant une approche correcte - vous vous en rendrez compte notamment en naviguant dans les unités pour les modifier et les déboguer.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X