Declaring an interface

In Delphi, we can declare an interface like so:

type
  ICalculator = interface(IInvokable)
    ['{9A60C8ED-CEB2-4E09-87D4-4A16F496E5FE}']
    /// add two signed 32 bit integers
    function Add(n1,n2: integer): integer;
  end;

It just sounds like a class definition, but, as you can see:

  • It is named ICalculator, and not TCalculator: it is a common convention to start an interface name with a I, to make a difference with a T for a class or other implementation-level type definition; 
  • There is no visibility attribute (no private / protected / public / published keywords): in fact, it is just as if all methods were published; 
  • There is no fields, just methods (fields are part of the implementation, not of the interface): in fact, you can have properties in your interface definition, but those properties shall redirect to existing getter and setter methods, via read and write keywords; 
  • There is a strange number below the interface name, called a GUID: this is an unique identifier of the interface - you can create such a genuine constant on the editor cursor position by pressing Ctrl + Shift + G in the Delphi IDE; 
  • But the methods are just defined as usual.

Implementing an interface with a class

Now that we have an interface, we need to create an implementation. 

Our interface is very basic, so we may implement it like this:

type
  TServiceCalculator = class(TInterfacedObject, ICalculator)
  protected
    fBulk: string;
  public
    function Add(n1,n2: integer): integer;
    procedure SetBulk(const aValue: string);
  end;
function TServiceCalculator.Add(n1, n2: integer): integer;
begin
  result := n1+n2;
end;
procedure TServiceCalculator.SetBulk(const aValue: string);
begin
  fBulk := aValue;
end;
You can note the following:
  • We added ICalculator name to the class() definition: this class inherits from TInterfacedObject, and implements the ICalculator interface; 
  • Here we have protected and public keywords - but the Add method can have any visibility, from the interface point of view: it will be used as implementation of an interface, even if the method is declared as private in the implementation class; 
  • There is a SetBulk method which is not part of the ICalculator definition - so we can add other methods to the implementation class, and we can even implement several interfaces within the same method (just add other interface names after like class(TInterfacedObject, ICalculator, IAnotherInterface)
  • There a fBulk protected field member within this class definition, which is not used either, but could be used for the class implementation.
  • Here we have to code an implementation for the TServiceCalculator.Add() method (otherwise the compiler will complain for a missing method), whereas there is no implementation expected for the ICalculator.Add method - it is perfectly "abstract".

Using an interface

Now we have two ways of using our TServiceCalculator class: 

  • The classic way;
  • The abstract way (using an interface).

The "classic" way, using an explicit class instance:

function MyAdd(a,b: integer): integer;
var Calculator: TServiceCalculator;
begin
  Calculator := TServiceCalculator.Create;
  try
    result := Calculator.Add(a,b);
  finally
    Calculator.Free;
  end;
end;

Note that we used a try..finally block to protect the instance memory resource.

Then we can use an interface:

function MyAdd(a,b: integer): integer;
var Calculator: ICalculator;
begin
  ICalculator := TServiceCalculator.Create;
  result := Calculator.Add(a,b);
end;

What's up over there?

  • We defined the local variable as ICalculator: so it will be an interface, not a regular class instance; 
  • We assigned a TServiceCalculator instance to this interface variable: the variable will now handle the instance life time; 
  • We called the method just as usual - in fact, the computation is performed with the same exact expression: result := Calculator.Add(a,b)
  • We do not need any try...finally block here: in Delphi, interface variables are reference-counted: that is, the use of the interface is tracked by the compiler and the implementing instance, once created, is automatically freed when the compiler realizes that the number of references to a given interface variable is zero; 
  • And the performance cost is negligible: this is more or less the same as calling a virtual method (just one more redirection level).

In fact, the compiler creates an hidden try...finally block in the MyAdd function, and the instance will be released as soon as the Calculator variable is out of scope. The generated code could look like this:

function MyAdd(a,b: integer): integer;
var Calculator: TServiceCalculator;
begin
  Calculator := TServiceCalculator.Create;
  try
    Calculator.FRefCount := 1;
    result := Calculator.Add(a,b);
  finally
    dec(Calculator.FRefCount);
    if Calculator.FRefCount=0 then
      Calculator.Free;
  end;
end;

Of course, this is a bit more optimized than this (and thread-safe), but you have got the idea.

There is more than one way to do it

One benefit of interfaces we have already told about, is that it is "orthogonal" to the implementation.

In fact, we can create another implementation class, and use the same interface:

type
  TOtherServiceCalculator = class(TInterfacedObject, ICalculator)
  protected
    function Add(n1,n2: integer): integer;
  end;
function TOtherServiceCalculator.Add(n1, n2: integer): integer;
begin
  result := n2+n1;
end;

Here the computation is not the same: we use n2+n1 instead of n1+n2... of course, this will result into the same value, but we can use this another method for our very same interface, by using its TOtherServiceCalculator class name:

function MyOtherAdd(a,b: integer): integer;
var Calculator: ICalculator;
begin
  ICalculator := TOtherServiceCalculator.Create;
  result := Calculator.Add(a,b);
end;

Here comes the magic

Now you may begin to see the point of using interfaces in a client-server framework like ours.

Our mORMot is able to use the same interface definition on both client and server side, calling all expected methods on both sides, but having all the implementation logic on the server side. The client application will transmit method calls (using JSON) to the server (using a "fake" implementation class created on the fly by the framework), then the execution will take place on the server (with obvious benefits), and the result will be sent back to the client, as JSON. The same interface can be used on the server side, and in this case, execution will be in-place, so very fast.

By creating a whole bunch of interfaces for implementing the business logic of your project, you will benefit of an open and powerful implementation pattern. Working from Delphi 6 up to XE2.

More on this later on... see this blog article and its follow-ups, including this Interface based Services in mORMot - Overview document.

Do not forget to take a new look at good principles of playing with interfaces: the SOLID design principles.

And comments are welcome on our forum, just as usual!