Interfaces - why are they recommended?

I’ve read a few times now that I should be using interfaces, but I’m really struggling to see why.

I think I understand how they work, but I just don’t see they would improve anything in my projects.

Surely defining a class with the functionality you need is the simplest and cleanest way?

Interfaces just seem to add another layer.

David, Nick Hodges book “Coding In Delphi” covers the subject quite well
(although a little old, it’s still a valuable resource).

My Server of choice (mORMot) uses this in its Service Oriented
Architecture extensively, and (and Nick’s book doesn’t cover this)
extends the technique to implement User Access Rights (very valuable -
and a clean, simple and powerful design concept).

Probably plenty of other references that others may be aware of …

Cheers
Robert

1 Like

Hi!

Let me enlighten you :smiley:
Even if you don’t consider all the other advantages, the single biggest win of interfaces is that laying down the wrong code is very, very, very difficult.

In practice, this means you could have code like this (I am using this in a commercial system, so it’s actual working code):

var
  TillObject: ITillObject;
  TillObjectProcessor: ITillObjectProcessor;
begjn
  TillObject := NewTillObject;
  TillObjectProcessor := ( TillObject as ITillObjectProcessor );
  if TillObjectProcessor.Count( function (Arg1:ITillPosition) : boolean 
                                                 begin 
                                                    Result = Arg1.Quantity <> 0;
                                                 end ) > 0 then
    // Do something
end;

ITillObjectProcessor has several, different methods to deal with all sorts of things by removing indexed loops (it’s a long story, I can’t rework literally everything because it’s complicated and ties in deeply within our own framework, which means I would have to check every single use of that class in the whole program… a mess!) and instead having a very limited surface that becomes difficult to get wrong.
Anyone coming to the code now will see what we do and will simply have to do the same kind of thing, no big room for error.

You may also have noticed in the code above how I am casting from one interface to another: that is known in the Delphi world as “aggregation” and can be achieved either by implementing both interfaces in the main object (which I highly discourage) or by declaring the two interfaces in the main object and then have an aggregate object (of class TAggregatedObject) implement the second interface, which is what I did.
Then in the main object you use the “implements” keyword and create the aggregated object in a private field which is then exposed as a protected property.

If you do the same for all other methods of the interfaces, your main class will look basically empty, which is a clue you shouldn’t use it directly but instead via an interface.

By using the interfaces, you have a very limited surface and therefore if you can’t do something that is a clue you’re using the wrong interface, which also means you have to ask someone what the right interface is: this fosters cross-pollination of knowledge even for those new to the system while making their contribution positive instead of negative even in the very first days.

Does this make more sense now?

A

1 Like

If you have access to the mail archive category (you should if you are a current adug member) then there was a good thread about this back in 2017

https://forums.adug.org.au/t/using-interfaced-objects/56849

If you can’t see it let me know and I’ll repost my answers here.

Hi David

One of the benefits I see of using Interfaces is that the user of the interface (the program calling the interface method) does not need to know what implemented the method.

For example, let’s assume you have the following code where you want to call the method TDavidsClass.DoSomething
Then your code could look like:

procedure TMainForm.Test;
begin


DavidsClassInstance.DoSomething


end

Now let’s assume that you want to use the same logic but instead of using TDavidsClass.DodSomething you wanted to use a method from another calll, say TAnotherDavidsClass.DoSomething.
To do this you would pass an instance of a class that BOTH TDavidsClass AND TAnotherDavidsClass inherit from. Lets assume that this class is called TDavidsBaseClass.
You would then have the following

type
TDavidsBaseClass = class(TXXXX)
public
procedure DoSomething; virtual; abstract;
end;

type
TDavidsClass = class(TDavidsBaseClass)
public
procedure DoSomething; override;
end;

type
TAnotherDavidsClass = class(TDavidsBaseClass)
public
procedure DoSomething; override;
end;

procedure TMainForm.Test(const xInstance: TDavidsBaseClass);
begin


xInstance.DoSomething


end

Now what if the only base class would be TObject. In this case you would need to add the abstract method DoSomething to TObject

Also, rather than considering only two classes, what would you do if there were many more classes and you wanted to share many more methods.

This is where I see Interfaces are very handy.
Rather than passing an instance of a class to the Test method you would pass an instance of the interface:

type
IDavidsInterface = interface
procedure DoSomething;
procedure DoSomething1;
procedure DoSomething2;
procedure DoSomething3;
procedure DoSomething4;
procedure DoSomething5;
procedure DoSomething6;
procedure DoSomething7;
procedure DoSomething8;
end;

procedure TMainForm.Test(const xInstance: IDavidsInterface);
begin


xInstance.DoSomething


end

The interface IDavidsInterface can be ‘attached’ to any class:

type
TXXXX = classe(TBaseClass, IDavidsInterface)

end;

Another benefit of using Interfaces is that class are reference counted and are automatically freed when the count goes to zero.

Consider the following

type
TDavidsClass = class(TXXX, IDavidsInterface)

end;

procedure xxxxxxxxxxxx.xxxxx;
var
ClassInstance: TDavidsClass;
InterfaceInstance: IDavidsInterface;
begin
/// reference count = 0
ClassInstance:= TDavidsClass.Create;
InterfaceInstance:= ClassInstance; // inc reference count, now 1


InterfaceInstance:= nil; // dec reference count,
// now 0 and so ClassInstance is freed but ClassInstance to set to null

ClassInstance.DoSomething; // would fail as the ClassInstance is undefined

end;

It is strongly recommended that you do not mix the use of classes and interfaces (which I have done above).

I would write code like:

procedure xxxxxxxxxxxx.xxxxx;
var
InterfaceInstance: IDavidsInterface;
begin
/// reference count = 0
InterfaceInstance:= TDavidsClass.Create; // inc reference count, now 1


InterfaceInstance:= nil; // dec reference count,
// now 0 and so Class instance is freed and no longer available for use

end;

So the benefits I see are

  • Automatic freeing when not used
  • No need to worry about finding/creating base classes
    Please note that this is my own view of interfaces.

Hope this helps.

Regards
Graeme

I bought the Coding in Delphi book earlier this week, hence my revisit of what Interfaces are.

To be honest when I read that thread from 2017 there seemed to be just as many people on each side of the argument (for and against).

I have lots of classes, many of them are used as objects in other classes as a list. Most of my classes have very little in way of common methods. The ones that do need it descend from a base class that has those methods and I override them (with inherited actions) as required for the specific use.

I’ll revisit the book and see if any of it makes more sense, but I suspect that my time is possibly better spent improving other areas of my code for now.

Hi David,

Ages ago when I worked on a large system built using an interface for every class, I found debugging a nightmare, as back in D7, there was no way to get to the underlying object to see the ‘real’ data it was holding. I believe some people include a reference to the underlying class to help this problem. Hopefully later versions of Delphi have overcome this difficulty!

Regards, Tony

No, it hasn’t and in a sense it’s a good thing.
I almost completely disagree on the way C# and Java go about it, i.e. being able to cast between the interface and the underlying object because that means leaking implementation details in the interface, which is rarely good.

On the other hand, I do agree that interfaces can be very hard to debug if you design the thing incorrectly: using TAggregateObject inherited classes, for example, is a great way to insulate implementations and make testing easier. It also means you have to work with interfaces only, which will highlight any design problems your original interface may have.

A

I’m a big user of interfaces but I agree with your statement above. Interfaces shoehorned in after the fact is almost certainly time that could be better spent elsewhere.

Next time you’re designing new code, if you find yourself designing a class hierarchy with new ancestor classes that have a lot of virtual or virtual abstract methods, that’s a good time to try experimenting with interfaces. You would define an interface that has all those virtual and virtual abstract methods you would otherwise have put in your ancestor class.

Even then it’s still going to feel a bit weird, like you could just do all this with classes. The ah-ha moment will come later such as when you decide you want to implement your interface with something that has a completely different ancestor than the other classes.

Interfaces are a paradigm shift and when you throw in the reference counting as well, a big paradigm shift. Don’t worry if the ah-ha moment never comes, classes already give you maybe 75% of what interfaces do (plus classes have other stuff that interfaces don’t).

I think my next task is to learn how use threads so some tasks in my application become non blocking, but I’ll save those annoying questions for another day and thread. :D.

There’s a lot of info/discussion on interfaces in the mail archive - also plenty of misconceptions and half truths :wink: Forget about the comments on COM, or bugs in the IDE with navigation etc. Focus on the benefits to your code, in terms of decoupling and refactoring ease.

You don’t have to implement interfaces for every class in your application!

One thing that I find others don’t do very often is use interfaces with forms and frames.

It’s always bugged me that that all components you drop on a form or frame are published - so visible to anything with a reference to the form or frame. Not a big deal in and of itself (and necessary for serialization of forms) - however over time it leads to (and perhaps encourages) coupling that is hard to unpick when refactoring.

So if I need to reference a form or frame in my applications, I will typically implement an interface that exposes only the api surface that part of the code needs to interact with the from or frame.

Say for example you have a form with a Memo control used for logging, and pass this form around to other parts of the application.

Eventually you will see code like this (a contrived example, in a real app it would probably be way worse).

LoggerForm.LogMemo.Lines.Add('error...');

Of course you could improve that by adding a method

LoggerForm.LogError('error....');

which is of course better - however there is still noting to stop you (or other devs, new hires etc) from accessing the memo directly.

There are two issues there, firstly the calling code really just wants log an error - it really shouldn’t care about how that is implemented - and secondly what happens when the log control is changed to a TSuperColoredAwesomeLogControl - then you are left updating code all over the show.

So how do you fix this - create an interface :wink:

type
  TLogLevel = (Debug, Info,Warning, Error);

  ILogger = interface
  ['{156AC1CA-E300-4F2B-ACF5-A14F8CA32859}']
    procedure Log(const level : TLogLevel; const value : string);
  end;

Implement this interface somewhere (doesn’t have to be a form, could be a class that writes to a text file - easy to change later)

type
  TLogFrame = class(TFrame, ILogger, IClipboardProvider)
    LogMemo: TMemo;
  private
    { Private declarations }
  protected
    procedure Log(const level: TLogLevel; const value: string);
  ....
end;

In this example, the logger is implemented on a frame on the main form, so we get a reference to ILogger

procedure TMainWindow.FormCreate(Sender: TObject);
begin
  FLogger := LogFrame1 as ILogger;
  ....
end;

Then when parts of the code need access to the log, we pass them an ILogger

procedure TMainWindow.Button2Click(Sender: TObject);
begin
  if AnotherForm = nil then
    AnotherForm := TAnotherForm.Create(Application, FLogger);
  AnotherForm.Show;
end;

In the above example, the TAnotherForm doesn’t care how ILogger is implemented, and it doesn’t need to know. It has no way to reference LogMemo since it can only what is exposed via the interface. This decouples the implementation of the logger from the use of it - and frees us to change how logging is implemented without breaking any calling code.

This is actually something I demoed during the Oct 2020 Canberra adug meeting - not sure if that was recorded? @StephenCorbett ?

This is the example I showed

In the future, if we were to implement dependency injection, we would just register the ILogger instance with the container and use constructor injection to inject the ILogger dependency in where ever it is needed.

I always find it easier to understand programming concepts with real world examples - sometimes those can be hard to find. In the package manager that I’m (still) working on, I use interfaces a lot, and I use dependency injection (Spring4D) - worth cloning and exploring the code to look at the above concepts in action.

3 Likes

As part of the major upgrade to my application, I have been converting all the calls to UI objects to call aptly named procedures instead. Being able to prevent direct access to UI objects would be nice (in due course), as that would really force a change in habit.

Vincent’s presentation can be viewed at ADUG Canberra October 2020 - TSVGIconImageList plus Interfaced Forms and Frames - YouTube

2 Likes

Starting at 53:00 ADUG Canberra October 2020 - TSVGIconImageList plus Interfaced Forms and Frames - YouTube

1 Like

I have watched that (section of) video now and think I’ll leave Interfaces for sometime further down the track.

Just a note that Marco has two ‘courses’ related to interfaces …

Yeah, you can. The as keyword was extended awhile back to let you cast back to TObjects.

http://docwiki.embarcadero.com/RADStudio/Sydney/en/Interface_References_(Delphi)#Casting_Interface_References_to_Objects

Not saying you should, mind you, but you can.

Cheers
Malcolm

4 posts were split to a new topic: Code Navigation

A post was merged into an existing topic: Code Navigation

Hi David,

several answers here and not a single word against interface usage. Looks like it’s too good to be true, right? My grandma used to say that if something looks too good to be true, it definitely is.

There are several CONS regarding interface usage:

  • Interfaces are a contract, and like real contracts they are hard to break. Whenever you need to add a new method or property to your interface, all implementations need to be changed. If you need to remove a deprecated method from an interface… well… forget it… When this involves 3rd parties (imagine that you are writing a library) it becomes a nightmare. The “solution” is to create a new interface and suddenly your object now implements 20+ interfaces (have a look at WinAPI.ADOInt.pas and you will know what I mean).

  • When your object implements a new interface, its instance size grows by the size of a pointer. So, if your object implements 4 interfaces, in x86, the InstanceSize will be 24 bytes instead of 8 bytes. If you follow the design of .NET classes for instance, where every core class like lists, dictionaries, etc., implement several interfaces (sometimes more than 10), the memory usage of your application will grow exponentially. This may be irrelevant for desktop applications, but it’s nasty for any server application.

  • Delphi IDE has poor support for Interfaces (opposed to Visual Studio, for instance). You just can’t determine what classes implement a specific interface. Code navigation becomes a nightmare inside Delphi IDE as well. Ctrl+Click just won’t take you to the object implementing the interface, but to the interface declaration (probably in another unit), so it becomes mostly useless. Be prepared to make Grep search your best friend…

  • Virtual method calls in interfaces are really slow. For instance, if you decide to write a TList which implements IList interface, a virtual Get() method will suddenly become many times slower than the direct method call. If the method is already slow, fine, the interface overhead won’t be significant. But in my previous example, it will definitely be.

  • Mixing object references and interfaces is in general a very bad idea which ends up with nasty bugs, memory leaks and all sort of problems.

  • Finally, also opposed to .NET implementation, Delphi interfaces carry a COM baggage, so you need to either descend your object from TInterfacedObject (there are a few other options in recent Delphi versions) or implement QueryInterface, _AddRef and _Release methods in every single class you write. If you decide to inherit from TInterfacedObject then you need to deal with the mixed memory management mode: some objects need to be freed explicitly, others don’t, which causes more problems than solves, IMO.

Sometimes all you need is an abstract class instead of an interface. For instance, if your class does not descends from a VCL/RTL specialized class and you only implement one single interface, probably you will be better with an abstract class.

My recommendation: think about all pros and cons (yes, they exist) well before going down that path.

1 Like