I'm working in Delphi XE, windows 7.
In an application I want to enable different report types for my users to select.
To do this, I have 1 base report class and a subclass per report type (xml, csv, ppt, etc).
{Just an illustrating example}
TBaseReport = class
public
constructor Create;
procedure GenerateReport; virtual; abstract;
class function ReportType: string; virtual; abstract;
end;
T*Report = class(TBaseReport);
//Etcetera.
What I want to do is use Rtti to detect all report classes and list their ReportType.
After that, I want to use Rtti to create an instance of the chosen report class and call GenerateReport. All in all, this is not too difficult to achieve.
However there is a major drawback: I'm never hard coding the use of the descending classes, so the code does not get included in the executable.
Is there a decent way to force the linker/compiler to include these classes?
A(n ugly) work around would be to simulate usage of the reports in their initialization section, but I'd rather not do that.
A better solution is to make the base class persistent and to call 'RegisterClass(T*Report);' in the initialization section. It works, but I do not see any other need to make them persistent, so again, I'd rather not do that. On the other hand, maybe this is the only way to do it?
Thanks in advance.
You can create your own version of RegisterClass. Something like RegisterReportClass. Internally you keep your own list of report classes that can be used. Your register function will take a TBaseReport class type - No need for TPersistent.
Your RegisterReportClass method should be called in the Initialization section making sure the classes are included.
If you look in the Graphics unit you can see TFileFormatsList = class(TList). This is the class that is used to hold the different Graphic Types and could be used as an example for creating your own TReportFormatsList. Delphi uses a static function TPicture.RegisterFileFormat to add items to their internal list.
You can use the {$STRONGLINKTYPES ON} Compiler Directive, to include all symbols of your app in the final exe, remember that this option increases the executable size, as more RTTI is included in the executable.
Related
I'm currently struggling with the following:
I need to create two different DLL's, which do exactly the same but are looking to a different DB. The two DB's are nothing alike.
My dll's should be handling the communication with those different DB's.
So that the main program chooses which dll he wants to use.
I want to be sure each dll has exactly the same procudes/functions/...
I was thinking of using interfaces.
But I can't figure out how to create global interfaces. the dll's belong to the same projectgroup.
I do believe you're making a "mountain out of a molehill" thinking you need 2 different DLLs. But if you choose the last of my suggested options, you should find it fairly easy to switch between a 2 DLL solution and 1 DLL solution.
Option 1
This is the most straightforward:
Create a new unit.
Add your DLL interface (the exports).
Include the unit in both projects.
unit DllExportIntf;
interface
uses
DllExportImpl;
exports DoX;
implementation
end.
Note that this unit uses DllExportImpl which will also have to be included in both projects. However, you'll need 2 different files with the same name in 2 different locations in your file system. So each DLL project will have different implementations.
Now whenever you make a change to your interface, your projects won't compile until you've updated each of the DllExportImpl units.
What I don't particularly like about this solution is the need for units with the same name but different behaviour. Since you intend having both DLLs in the same project group: I should warn you that I've experienced the IDE getting confused by duplicate unit names.
Option 2
Place the exports into a shared include file.
library DllSharedExportsImpl1;
uses
DllExportImpl1 in 'DllExportImpl1.pas';
{$I ..\Common\DllExports.inc}
The DllExports.inc file will only include your exports clauses. E.g.
exports DoX;
This has the advantage that now each DLL can use different unit names for the different implementations. And if you change your include file, neither project will compile until you've updated its implementation unit to accommodate the change.
Note that this does come with its own set of problems. The way includes work: the compiler effectively shoves the contents of the include file into the unit at compile time. So what looks like line 7 to the IDE is entirely different to the compiler. Also editing include files can be a bit of a nuisance because context can only be determined where the file is included making editor support quite impractical.
Option 3
This option is a little more work, but provides much better long-term maintainability.
You do this by implementing your interface via polymorphic objects. In this way, both DllProjects will also share the routines that are actually exported. When each DLL initialises, it sets the concrete implementation to be used.
Your DLL interface could look something like this.
unit DllExportIntf;
interface
type
TAbstractImpl = class(TObject)
public
procedure DoX; virtual; abstract;
end;
procedure AssignDllImpl(const ADllImpl: TAbstractImpl);
procedure DoX;
exports DoX;
implementation
var
GDllImpl: TAbstractImpl;
procedure AssignDllImpl(const ADllImpl: TAbstractImpl);
begin
if Assigned(GDllImpl) then
begin
GDllImpl.Free;
end;
GDllImpl := ADllImpl;
end;
procedure DoX;
begin
GDllImpl.DoX;
end;
end.
When you initialise your DLL, you can call:
AssignDllImpl(TDllImpl_1.Create);
A clear advantage of this approach is that if there is any common code between your 2 DLLs, it can be included in your base implementation. Also, if you can change an existing method DLL in such a way that it does not require a change to TAbstractImpl, you possibly will only need to recompile your DLLs.
Furthermore, if you need to change existing virtual abstract methods, you will have to update the overrides in your concrete implementations accordingly.
WARNING If you add a new virtual abstract method, your projects will still compile with warnings that you are creating objects with abstract methods. However, you should always treat warnings as errors. If you do, this caveat won't be a problem.
NOTE: As mentioned earlier, using this approach you should be able to fairly easily switch between single DLL and 2 DLL solutions. The difference basically boils down to which units are included in the project, and how you initialise the global.
It may also be worthwhile mentioning that you could even eliminate the global altogether by implementing a Handle to use with each of your DLL routines. (Similar to Windows.) Bear in mind that there are technical issues when trying to pass objects between DLL and application code. This is why instead of passing objects, you use a "handles" to objects and encapsulate the actual object instances internally.
Considering all that was said, I believe that you would be more successful if you design your solution with packages, not DLLs. A package is a DLL, but rich in symbols, so Delphi can be a better use of it. Particularly, the symbols declared inside the package will more easily be loaded by your application, with a much higher level of abstraction. It´s what the Delphi IDE uses to load components.
So, following this design, this is what you have to do:
Declare your interfaces in units existing in a package named (for instance) DBServices.dpk. Here is an example of such an unit:
unit DBService1;
interface
uses
....;
type
IService1 = interface
[....] // here goes the GUID
procedure ServiceMethod1;
procedure ServiceMethod2;
// and so on...
end;
implementation
end.
So, above you created an unit that declares an interface. Your aplication can use that interface anywhere, just reference the package in your application and use it in other units and you will have the access to the symbols declared.
Declare the implementation class for that very same interface in another unit of another package, for instance, dedicated to SQLServer (SQLServerServices.dpk):
unit SQLServerService1;
interface
uses
DBService1, ....;
type
TSQLServerService1 = class(TInterfacedObject, IService1)
protected // IService1
procedure ServiceMethod1;
procedure ServiceMethod2;
// and so on...
end;
implementation
procedure TSQLServerService.ServiceMethod1;
begin
// Specific code for SQL Server
end;
procedure TSQLServerService.ServiceMethod2;
begin
// Specific code for SQL Server
end;
...
end.
Above you declared an implementing class for the interface IService1. Now you have two packages, one declaring the interfaces and other implementing those interfaces. Both will be consumed by your application. If you have more implementations for the same interfaces, add other packages dedicated to them.
One important thing is: you have to have a factory system. A factory system is a procedure ou class that will create and return the implementations for your application from each package.
So, in terms of code, in each service package (the ones that implement the interfaces) add a unit named, for instance, xxxServiceFactories, like this:
unit SQLServerServiceFactories;
interface
uses
DBService1;
function NewService1: IService1;
implementation
uses
SQLServerService1;
function NewService1: IService1;
Result := TSQLServerService1.Create;
end;
end.
The code above declares a function that creates the SQL Server implementation and returns it as an interface. Now, if you call a method from the interface returned, you will be actually calling the specific implementation of it for SQL Server.
After loading the package, you will have to link to that function in the very same way you would do if working if a DLL. After you have the pointer for the function, you can call it and you will have the interface in your application's code:
...
var
service1: IService1;
begin
service1 := NewService1;
service1.ServiceMethod1; // here, calling your method!
end;
The model I described in this answer is the one I used in a similar scenario I had to deal with in the past. I presented general ideas that work, but you have to understand the fundamentals of packages and interfaces to really master the technique.
A comprehensive explanation on those matters would be very long for an answer here, but I guess it will be a good starting point for you!
What you want to do is create a COM component project. Define your methods on that & implementations for one DB. Then create a second COM component that uses the same interface.
On the off-chance that your question is more about the fundamentals of Delphi, I've added another answer which may be more helpful to you than the first one. My first answer focused on getting 2 DLLs to expose the same methods (as per the main body of your question). This one focuses on the last 2 sentences of your question:
But I can't figure out how to create global interfaces. The dll's belong to the same project group.
Based on this, it sounds like you're looking for an option to "mark an interface as global so that projects in the same group can use them". Delphi doesn't need a special feature to do this because it's trivially available if you understand certain fundamental principles.
When you create a new unit, it is by default added to the current project. However if you want to share the unit between multiple projects, it's a good idea to save it to a different folder so it's easy to see that it's shared. Your first DLLs project file should look something like this.
library Dll1;
uses
DllSharedIntf in '..\Common\DllSharedIntf.pas';
You can define your "global" interface in the DllSharedIntf unit. E.g.
unit DllSharedIntf;
interface
type
IDllIntf = interface
['{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}']
procedure DoX;
end;
implementation
end.
NOTE: Because the interface type is declared in the interface section of the unit, it is considered "global" because other units are able to use it. But this doesn't automatically make it available to other projects.
You now have to add the shared unit to your other project so it becomes available for use by other units in that project. To do this:
Activate Dll2
Select Project and Add to Project...
Find DllSharedIntf and add it.
Delphi will automatically update your project source file to include the unit.
library Dll2;
uses
DllSharedIntf in '..\Common\DllSharedIntf.pas';
Now in each DLL project you can add a separate implementation unit. E.g. For Dll1:
unit DllImpl1;
interface
uses
//It's very important to indicate that this unit uses the shared unit.
//Otherwise you won't be able to access the "global types" declared
//in the interface-section of that unit.
DllSharedIntf;
type
TDllImpl1 = class(TInterfacedObject,
//Any types defined in the interface-section of any units that
//this unit **uses**, can be accessed as if they were declared
//in this unit.
IDllIntf)
protected
//The fact that this class is marked as implementing the IDllIntf
//means that the compiler will insist on you implementing all
//methods defined in that interface-type.
procedure DoX;
end;
implementation
NOTE This answer only covers sharing an interface between projects. You'll still need to expose the functionality of the DLLs via appropriate exports. You'll need an approach similar to option 3 of my other answer.
Summary
We don't usually talk about "global interfaces" in Delphi. It's generally understood that anything declared in the interface section of a unit is globally accessible. (We do make more of an issue about global variables due to their dangers though; but that's an entirely different topic.)
In Delphi:
Whenever you want one unit (A) to make use of functionality defined in another unit (B), you need to add unit B to the uses clause of unit A.
Whenever you want a project to use a unit created in another project, you need to add the unit to the project. (TIP: It's a good idea to put such units in a separate folder.)
NOTE: When sharing units between projects, the project group is actually irrelevant. Projects don't need to be in the same group to share units. All you need to do is ensure the project can access the unit so that other units in your project can uses it.
I am in the progress of trying to learn a few new tricks in order to better organize some of the sourcecode in my units in Delphi.
I have noticed that some functions or methods I access appear to be classes inside classes, but I have not yet been successful in making a working class inside a class, although it compiles fine, I still get error messages when executing the code.
What I would like to do is to make kind of a tree with functions, procedures and values to set or get. I would be grateful if somebody could help me out a little bit with an example.
I have today some classes that are Types.
I then assign the types to a variable:
something=TSomething
and Then for something to happen I write "something.action".
My aim is to go further, and define sub-functions or/and sub-procedures.
Lets say I have three or four classes. TSnippet, TAction1, TAction2, TSubAction1, Etc.
I would like to use or assign these to a single variable and use them like:
Snippet.Action1.SubAction1.Organize(param1,param2);
Snippet.Action2.SubAction2.Returns='SomeString';
Snippet.Action1.SubAction1.SomeProcedure;
Is anybody able to help me with a useful example as in how to write code for this approach to work?
And also.. does anybydy know how such an implementation of code will affect CPYCycles needed in order to execute code versus the old fashioned method of having thousands of procedures with all different names, but more direct access (it feels like more direct access).
As of my first text was maybe a bit unclear, this follows.
I would like to make use of the editors automatic suggestions of procedures/functions available in order to simplify programming a little bit.
I started to make a Class for this, and it works great.
Consider a classname "Data". What can we do with data? We can Edit, Add, Delete, Save and Load.
Ok. This is my first Class.
Consider then another Class: "Encrypt". We can do DES, DES3, HASH, BITSHUFFLE.
I can go on with a third Class: "Compress". We can do LZW, ZIP, RAR, MP3, MPG, MP4, etc.
Instead of using these as 3 different classes, I would like to combine them in one, yet keeping them separate.
I would like to make a kind of an OwnerClass for the other classes. We can call this "MyStuff"
Whenever I type "MyStuff." in the editor, I should get up a list of "Data, Encrypt, Compress". Further, When I then choose "Compress", the next list for that class' procedures and functions will list up.
The classes may have some local variables, but the main functionality will be towards global arrays.
Maybe there are other ways of achieving this. I don't know.
My basic aim is to be able to categorize and put together routines that belong together.
This is already done in Units, but this does not help with the automatic list from the editor.
Thank you in advance.
Morten.
I think I understand what it is you're asking, after your edit.
What you're calling "classes in classes" are called properties and methods; they're other classes, variables, or procedures/functions that are declared in their containing class. The "list" you're talking about is called Code Insight, and it shows you the available properties and methods of the class you're referencing in your code at that point.
This should do something like you describe, and give you an idea of how to implement it in your own code:
unit MyStuffUnit;
interface
uses
SysUtils;
type
TEncryptionType = (etDES, etDES3, etHASH, etBITSHUFFLE);
TMyStuffEncryption = class(TObject)
private
FEncryptType: TEncryptionType;
public
constructor Create;
published
property EncryptionType: TEncryptionType read FEncryptType
write FEncryptType;
end;
TCompressionType = (ctLZW, ctZIP, ctRAR, ctMP3, ctMPG, ctMP4);
TMyStuffCompression = class(TObject)
private
FCompressionType: TCompressionType;
public
constructor Create;
published
property CompressionType: TCompressionType read FCompressionType
write FCompressionType;
end;
TMyStuff = class(TObject)
private
FCompression: TMyStuffCompression;
FEncryption: TMyStuffEncryption;
public
constructor Create;
destructor Destroy; override;
published
property Compression: TMyStuffCompression read FCompression
write FCompression;
property Encryption: TMyStuffEncryption read FEncryption
write FEncryption;
end;
implementation
constructor TMyStuffEncryption.Create;
begin
inherited;
FEncryptType := etDES;
end;
constructor TMyStuffCompression.Create;
begin
inherited;
FCompressionType := ctLZW;
end;
constructor TMyStuff.Create;
begin
inherited;
FCompression := TMyStuffCompression.Create;
FEncryption := TMyStuffEncryption.Create;
end;
destructor TMyStuff.Destroy;
begin
FCompression.Free;
FEncryption.Free;
inherited;
end;
end.
If you create an instance of TMyStuff in your code, you should be able to type MyStuff. and get the option of choosing Compression or Encryption. Choosing Compression should allow you to set the CompressionType property.
This should be enough to get you going. :-) Remember that Delphi includes the source code for the VCL and RTL in almost all editions, so you always have that to look at for examples. (They're not always the very best examples, but they should give you ideas on how to do things.)
first of all, sorry if the title is a bit confusing.
i'm planning to develop a small erp application. this apllication will use plugins/addons. this addon might add or extend some modules of the base application.
for example, i have TCustomer class with property "id", and "name".
addon 1 will add a property "dateofbirth".
addon 2 will add a property "balance" and a method "GetBalance".
addon 1 and addon 2 are not aware of each other. addon 1 might be installed and not addon 2 or vice versa. so both addons must inherit the base tcustomer class.
the problem is when both addon are installed. how do i get extended properties in both addons? i will also have to extend the form to add controls to show the new properties.
can it be done using delphi? what is the best way to achieve this? maybe you can point me to some articles of examples?
thanks and sorry for my poor english
Reynaldi
Well, as you are already aware, you can't have more than one plug-in extend an existing class by inheritance. It would confuse the heck out of any app, including any programmer's dealing with the code.
What you need is some type of register mechanism in your TCustomer class where every plugin can register its specific properties, or provide a couple of call back functions for when a TCustomer instance is created (initialized), loaded, stored, or deleted. The core TCustomer after all really doesn't need to know more about the plug-in's than the fact that they might exist.
Depending on how you intend to load/store your data, the core TCustomer class doesn't even have to be aware of the extensions. It would be quite sufficient to make the persistence mechanism aware of plug-ins and provide a way for them to register a call back function to be called whenever a TCustomer / TOrder / TWhatever is initialized / loaded / saved / deleted.
You will also have to make the GUI aware of the plugins and provide the means for them to register pieces of UI to have the main GUI create an extra tab or some such for each plug-in's specific controls.
Sorry no code example. Haven't yet implemented this myself, though I thought about the design and it is on my list of things to play around with.
That said, to give you an idea, the basic mechanism could look something like this:
TBaseObject = class; // forward declaration
// Class that each plug-in's extensions of core classes needs to inherit from.
TExtension = class(TObject)
public
procedure Initialize(aInstance: TBaseObject);
procedure Load(aInstance: TBaseObject);
procedure Store(aInstance: TBaseObject);
procedure Delete(aInstance: TBaseObject);
end;
// Base class for all domain classes
TBaseObject = class(TObject)
private
MyExtensions: TList<TExtension>;
public
procedure RegisterExtension(const aExtension: TExtension);
procedure Store;
end;
procedure TBaseObject.RegisterExtension(const aExtension: TExtension);
begin
MyExtensions.Add(aExtension);
end;
procedure TBaseObject.Store;
var
Extension: TExtension;
begin
// Normal store code for the core properties of a class.
InternalStore;
// Give each extension the opportunity to store their specific properties.
for Extension in MyExtensions do
Extension.Store(Self);
end;
Such an "evolving" class is some kind of multi-inheritance.
I think you shall better use interfaces instead of classes.
That is, each plug-in will serve a class implementation, but you will work with interfaces, and an interface factory.
See this article about "why we need interfaces", or this article from our blog about interfaces and a comparison with classes.
You can test if a class implements an interface: for a plug-in system like yours, this is probably the best way to implement an open implementation. Duck typing is very suitable for plug-ins. The whole Delphi IDE (and Windows itself) is using interfaces for its plug-in systems (via COM for Windows). For a less strong implementation pattern, you can use not interfaces, but late bindings: see the answer of this SO question.
Take a look at the SOLID principles, especially the single responsibility principle. Your question directly breaks this principle: you attempt to mix client personal information (like name) and accounting (like a balance). If your project grows up, you'll probable be stuck by such a design.
Google is useless for these sorts of searches, because you get hundreds of millions of results absolutely none of which relate to the specific question.
The question is simply this:
Is it possible to have a Class Reference Property in Delphi?
If so, how?
Here's what I've tried...
type
TMyObject = class
// ...
end;
TMyObjectClass = class of TMyObject
TMyObjectA = class(TMyObject)
// specifics here
end;
TMyObjectB =class(TMyObject)
// specifics here
end;
TMyComponent = class(TComponent)
private
FObjectType: TMyObjectClass;
published
property ObjectType: TMyObjectClass read FObjectType write FObjectType;
end;
The above code compiles fine, however the Object Inspector does not show the ObjectType property at all.
My objective here (if you haven't already guessed) is to make it so that I can select a class descendant from a specific base class, to make the same component behave in a different way.
I want to do it this way so that the component doesn't need to know about the sub-classes directly (it needs to be fully modular).
Let me just make this bit clear: I cannot use an Enum to choose between the sub-class types as the component cannot directly link to the sub-class types (It's simply not possible in this particular case)
Anyway... thanks in advance!
You can find all classes that descend from a particular base class: Delphi: At runtime find classes that descend from a given base class? and make this a special property with list of values using TPropertyEditor.
If you were going to do this then you would need to provide a property editor. The IDE does not come with property editors for class type properties. You would also need to handle .dfm persistence. You would write the class type out to the .dfm file as a string and when the .dfm file is read, you would need to fixup the reference. New style RTTI could do that.
However, I don't think any of this is actually viable for the following reason. Your design time code runs in a package inside the IDE and does not have access to the class types in the active project in the IDE. Those class types only exist when that project runs. So the ObjectType property in the code in your question cannot be assigned to anything meaningful in the design time package. Well, you could use it for classes defined in the VCL and any other packages installed in your IDE but I rather imagine you'd want to use it on classes defined in the active project.
I think all this means that you should instead use a simple string property and fixup the class type references only at runtime.
Am separating my delphi code into interface and implementation units ie.
EmployeeIntf.pas looks like this
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;
Then i have the two units ScheduleIntf.pas & DepartmentIntf.pas which declare the TScheduleList class and TDepartment class.
Then in my main unit which combines all the units looks like this,
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.
When i compile this the compiler throws an error;
*Type TScheduleList is not yet completely defined*
How can i have this classes separate in each unit file (.pas) and then do forward declarations without the compiler throwing this error?
The class themselvs are huge and i would prefer to separate them this way.
Gath
My first advice: Skip this $Include thing altogether. As Uwe wrote find a more Delphi-like solution.
If you really want to stay with the $Include style: The error you quote occurs because forward declarations don't work across "type" blocks. You forward declare TScheduleList in one block but define it in a different block. To cure this omit the "type" keyword in your *Intf.pas's and insert it in BusinessDomain.pas before the includes.
I'm afraid, there is no possibility to split a class declaration into multiple files. If the classes are that large, you should consider a redesign.
Another alternative are Interfaces:
type
IEmployee = interface
{ public properties and methods of an employee }
...
end;
type
TEmployee = class(BDObject, IEmployee)
...
end;
The interface and class declaration can now reside in different files.
Ulrich is quite correct (+1) whilst you can manipulate your include files to work approx this way, it's not going to be a very good Dev experience for you.
Think of the $Include directive as a simple text replacement mechanism, the things that make your units, well...units is the scoping mechanisms (uses sections, type sections etc) they will be much more difficult to use in an include file, thats why include files are usually called xxx.inc and not xxxx.pas as they often will not stand up as a source file by themselves. This of course makes them very difficult in a dev environment as they are then effectively just text files not proper debuggable units.
Include files are one of those legacy pascal features which keep rolling forward. There was a point when we didn't have a uses clause, and it was the only way to manage multiple files (of course we all programmed using wordstar commands for code navigation... wait, they are still there too).
Today, the most common use of include files is to include a block of code which must be shared among multiple files and can't be just used from another unit. As Mason pointed out in another comment, that would be the IFDEF-DEFINE blocks for determining things like what compiler options should be turned on, and what defines should be enabled project wide. We are no longer bound by the 64k limit on a source file.
Some other points to consider. Most of the tools for searching your source may not navigate into your include files. Using something as simple as a text search to find that text message that keeps popping up might be difficult. You would be much better served by not putting any code in them at all. When translating a map file to code, I believe your line numbers specified in the map file would be the total file if the include file was merged in place. If you use an automated tool such as MadExcept the error line reported might not be the actual location.
My suggestion would be to use interfaces as Uwe suggested. They are not just for COM, and can solve your desire to separate the "interface" from the "implementation".
Maybe my previous comment was a little presumptuous ... Reading your question again I think you may have misunderstood how to use units and in particular the "uses" directive.
You can declare individual classes both interface and implementation in a single unit file:
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;
Then later to use them elsewehere:
Unit BusinessDomain
interface
uses EmployeeDBCLassesU; // MY units needed by this unit
.
.
.
This brings all the class definition in to BusinessDomain
and you can then do
TBusinessDomain = class(BDDBobject)
EmployeeList: TEmployeeList;
EmployeeDM: TEmployeeDM;
.
.
.;
end;
Hope this helps more as you will gain so much from the correct approach - you will realise this especially when navigating units for editing and Debugging.
If you really like to separate interface and implementation, have a look at Modula2. This is also a pascal like application, but it uses two files for each "unit". One for the interface and one for the implementation.
Another solution, is to split the files or class definitions, and write a custom preprocessor that links these (text wise) together. You then have something like:
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.
With Delphi 2009 you can issue commands before and after the build phase. So technically this is possible.