在delphi中分离接口类和实现类?
我将我的delphi代码分为接口和实现单元,即 EmployeeIntf.pas看起来像这样在delphi中分离接口类和实现类?,delphi,class,Delphi,Class,我将我的delphi代码分为接口和实现单元,即 EmployeeIntf.pas看起来像这样 type // forward declaration TScheduleList = class; TDeparment = class; TEmployee = class(BDObject) .... function GetSchedules: TScheduleList; function GetDepartment: TDepartment; end;
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)中分离这些类,然后在编译器不抛出此错误的情况下进行转发声明
类本身是巨大的,我更喜欢这样把它们分开
Gath恐怕不可能将一个类声明拆分为多个文件。如果类很大,则应该考虑重新设计。 另一种选择是接口:
type
IEmployee = interface
{ public properties and methods of an employee }
...
end;
type
TEmployee = class(BDObject, IEmployee)
...
end;
接口和类声明现在可以驻留在不同的文件中。我的第一个建议是:完全跳过$Include。正如Uwe所写,找到一个更像Delphi的解决方案 如果你真的想继续使用$Include样式:你引用的错误是因为转发声明不能跨“type”块工作。在一个块中转发声明TSScheduleList,但在另一个块中定义它。要解决此问题,请在*Intf.pas中省略“type”关键字,并将其插入include之前的BusinessDomain.pas中。Ulrich是非常正确的(+1)虽然您可以操纵include文件以近似这种方式工作,但这对您来说并不是一个很好的开发体验
把$Include指令想象成一种简单的文本替换机制,使您的单元成为一种作用域机制(使用节、类型节等),它们在包含文件中使用起来要困难得多,这就是为什么include文件通常被称为xxx.inc而不是xxxx.pas,因为它们本身通常无法作为源文件。这当然使它们在开发环境中变得非常困难,因为它们实际上只是文本文件,而不是合适的可调试单元。也许我之前的评论有点冒昧。。。再次阅读您的问题,我认为您可能误解了如何使用单位,尤其是“使用”指令 可以在单个单元文件中声明接口和实现的各个类:
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;
希望这能给您带来更多帮助,因为您将从正确的方法中获益匪浅-您会意识到这一点,尤其是在导航单元进行编辑和调试时。如果您真的想将接口和实现分开,请查看模块2。这也是一个类似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.
使用Delphi2009,您可以在构建阶段前后发出命令。因此,从技术上讲,这是可能的。包含文件是pascal的传统功能之一,它不断向前发展。曾经有一段时间我们没有uses子句,这是管理多个文件的唯一方法(当然,我们都使用wordstar命令进行代码导航编程……等等,它们仍然存在) 今天,include文件最常用的用法是包含一个代码块,该代码块必须在多个文件之间共享,不能仅从另一个单元使用。正如Mason在另一篇评论中指出的,这将是IFDEF-DEFINE块,用于确定应该打开哪些编译器选项,以及应该在项目范围内启用哪些定义。我们不再受源文件64k限制的约束 还有一些其他的问题要考虑。大多数用于搜索源的工具可能无法导航到包含文件。使用像文本搜索这样简单的方法来查找不断弹出的文本消息可能很困难。如果不在它们中添加任何代码,您会得到更好的服务。在将映射文件转换为代码时,我相信如果include文件合并到位,则映射文件中指定的行号将是整个文件。如果使用自动化工具(如MAD),则报告的错误行可能不是实际位置
我的建议是使用Uwe建议的接口。它们不仅适用于COM,而且可以解决您将“接口”与“实现”分离的愿望 它根据您的第二个建议工作,从*Intf.pas中删除type关键字。谢谢。我知道你有一个适合这种布局/风格的解决方案,但我准备花钱让你以后把它改回真正的Delphi方式:)你失去了代码自动化和导航的便利性。Tim分离实际上使整个应用程序易于导航,业务对象不仅通过类在逻辑上分离,而且通过单元分离在物理上分离,也很容易找到他们和naviagte。让我们在6个月后再次进行此对话:-)与$Include解决方案相比,自定义预处理器的优势是什么?我认为这是一个品味问题。Include文件并不真正遵循delphi的精神。使用预处理器,您可以或多或少地扩展该语言。Include文件非常适合设置IFDEF-DEFINE块以进行配置,但如果您在其中有任何实际代码,则通常会出错。@Mason Wheeler:两个词“遗留代码”。我们正在与IFDEF作战,但这不是一场容易的战争。IFDEF有什么问题?它们通常是保持与多个版本的Delphi兼容的唯一方法。(或者让同一个单元与两个要求稍有不同的程序一起工作