在delphi中分离接口和实现类?

时间:2009-06-18 05:57:22

标签: delphi class

将我的delphi代码分离为接口和实现单元,即。

EmployeeIntf.pas看起来像这样

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;

然后我有两个单位 ScheduleIntf.pas & DepartmentIntf.pas ,它声明了TScheduleList类和TDepartment类。

然后在我的主单元中,它结合了所有单位,就像这样,

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.

编译时,编译器会抛出错误;

*Type TScheduleList is not yet completely defined*

如何在每个单元文件(.pas)中分离这些类,然后在没有编译器抛出此错误的情况下执行转发声明?

班级itselvs是巨大的,我宁愿这样分开他们。

加特

6 个答案:

答案 0 :(得分:15)

我的第一个建议:完全跳过这个$包含的东西。正如Uwe所写,找到了更像Delphi的解决方案。

如果您确实希望继续使用$ Include样式:引用的错误是因为前向声明不适用于“type”块。您转发在一个块中声明TScheduleList,但在不同的块中定义它。要解决此问题,请在* Intf.pas中省略“type”关键字,并在包含之前将其插入BusinessDomain.pas中。

答案 1 :(得分:12)

我担心,不可能将类声明拆分成多个文件。如果类很大,你应该考虑重新设计。

另一种选择是接口:

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

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

接口和类声明现在可以驻留在不同的文件中。

答案 2 :(得分:7)

Ulrich非常正确(+1)虽然您可以操作包含文件以便以这种方式工作,但对您来说这不是一个非常好的Dev体验。

将$ Include指令想象成一个简单的文本替换机制,使你的单位,好......单位是范围机制(使用部分,类型部分等),它们将更难以用于包含文件,这就是为什么包含文件通常被称为xxx.inc而不是xxxx.pas,因为它们通常不会自己作为源文件。这当然使它们在开发环境中非常困难,因为它们实际上只是文本文件而不是适当的可调试单元。

答案 3 :(得分:2)

包含文件是继续前进的传统pascal功能之一。有一点我们没有使用条款,这是管理多个文件的唯一方法(当然我们都使用wordstar命令编程进行代码导航......等等,它们仍然存在)。

今天,包含文件最常见的用途是包含一段代码,这些代码必须在多个文件之间共享,而不能只能在其他单元中使用。正如梅森在另一篇评论中指出的那样,那将是IFDEF-DEFINE块,用于确定应该打开哪些编译器选项,以及应该在项目范围内启用哪些定义。我们不再受源文件的64k限制的约束。

其他一些要考虑的问题。用于搜索源的大多数工具可能无法导航到您的包含文件。使用像文本搜索这样简单的东西来查找不断弹出的文本消息可能很困难。没有在其中放入任何代码,你会得到更好的服务。在将地图文件转换为代码时,我相信如果包含文件合并到位,则地图文件中指定的行号将是总文件。如果您使用MadExcept等自动工具,则报告的错误行可能不是实际位置。

我的建议是使用Uwe建议的界面。它们不仅适用于COM,而且可以解决您将“接口”与“实现”分开的愿望。

答案 4 :(得分:1)

也许我以前的评论有点冒昧......再次阅读你的问题我认为你可能误解了如何使用单位,特别是“使用”指令。

您可以在单个单元文件中声明单个类的接口和实现:

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;

然后再使用它们:

Unit BusinessDomain

interface

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

这会将所有类定义带入BusinessDomain

然后你可以做

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

希望这会有所帮助,因为您将从正确的方法中获得更多收益 - 尤其是在导航单元进行编辑和调试时,您会意识到这一点。

答案 5 :(得分:1)

如果您真的想分离界面和实现,请查看Modula2。这也是类似于pascal的应用程序,但它为每个“单元”使用两个文件。一个用于接口,一个用于实现。

另一个解决方案是拆分文件或类定义,并编写一个自定义预处理器,将这些文本链接在一起(文本方式)。然后你有类似的东西:

unit BusinessDomain
interface
uses
 classes;

type
  Employment = class from Interface\EmployeeIntf.pas;
  Department = class from Interface\DepartmentIntf.pas;
  Schedule = class from Interface\ScheduleIntf.pas;

implementation
uses
  SysUtils;

external define
  Employment = class from Implementation\EmployeeImpl.pas;
  Department = class from Implementation\DepartmentImpl.pas;
  Schedule = class from Implementation\ScheduleImpl.pas;
end;

end.

使用Delphi 2009,您可以在构建阶段之前和之后发出命令。从技术上讲,这是可能的。