Wednesday, 12 December 2007

Delphi interfaces and implementation

One of the things that makes Delphi unique is the division of all source code files ("unit"s) into several sections: interface, implementation, initialization and finalization.

Everything that other units need to know, is put into the interface section at the top. This reduces the time it takes to understand how to use a file significantly - you don't have to scroll through implementation details. The interface section contains const, function declarations and types, but no statements. Class members are declared, but methods are not implemented here.

The implementation section contains the implementation of all the items from the interface section. The initialization section contains statements that should be run before the application starts, which are local to this unit, and the finalization section can clean up resources in a similar way.

Delphi's compiler contains many neat features to enable faster compilation. For instance, a change in the implementation section, or a change of a typed const in the interface section, will not enforce recompilation of other source code files. Only significant changes in the interface section will make other parts of the source code recompile.

You can benefit a lot from these sections, if you manage to keep the interface section small, making sure that it has few lines of code. If a file has one function, and nothing else, in its interface section, it is much easier to use than if you have a huge class type with lots of private and protected members in the interface section. Unfortunately, this also means that the full benefits are not achieved if you do OOP the traditional way. Sometimes, it even makes sense to write a small unit, which has a very simple interface section, but where all implementation is about making calls to another unit that has a very difficult interface section.

Example:

unit SimpleApi;

interface

function CalculateSomething (parameter:string):string;



implementation

uses
SysUtils, ComplexApi; // Which other source files are used/linked

function CalculateSomething (parameter:string):string;
var
object:TComplexApi;
begin
object:=TComplexApi.Create;
try
Result:=object.CalculateSomething (parameter);
finally
FreeAndNil (object);
end;
end;


initialization
// This is where you could write code to initialize
// something for this unit at application start


finalization
// This is where you could write code to clean up stuff
// after the application has stopped running


end.

3 comments:

Anonymous said...

Never use the uses clause in the implementation section for anything except avoiding circular references.

There is no guarenteed order of unit initialization in the implementation uses, and if you reference anything from such a unit in the initialization section, that unit could be as yet uninitialized.

Only units listed in the implementation uses clause are guarenteed to be initialized first.

Jolyon Smith said...

Have to disagree - it should be:

Never use the interface uses section for anything that isn't strictly needed in the interface.

And never, ever, rely on unit initialization order.

Lars D said...

I made some tests, and as long as you don't have recursive dependencies between units, initialization sections are executed in correct order.