Recent

Author Topic: When is it useful for a property to implement an interface?  (Read 329 times)

Hansvb

  • Hero Member
  • *****
  • Posts: 922
I'm experimenting a bit with interfaces and ran into the example below. A property can implement an interface. When is something like this useful?

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  9.   logger.model;
  10.  
  11. type
  12.   { TForm1 }
  13.   TForm1 = class(TForm)
  14.     Button1: TButton;
  15.     Memo1: TMemo;
  16.     procedure Button1Click(Sender: TObject);
  17.   private
  18.   public
  19.   end;
  20.  
  21. var
  22.   Form1: TForm1;
  23.  
  24. implementation
  25.  
  26. {$R *.lfm}
  27.  
  28. { TForm1 }
  29. procedure TForm1.Button1Click(Sender: TObject);
  30. var
  31.   lSomeClass: TSomeClass;
  32. begin
  33.   Memo1.Clear;
  34.  
  35.   lSomeClass:= TSomeClass.Create;
  36.   try
  37.     Memo1.Lines.Add(lSomeClass.logger.Log('Via object property'));
  38.   finally
  39.     lSomeClass.Free;
  40.   end;
  41. end;
  42.  
  43. end.    

Code: Pascal  [Select][+][-]
  1. unit logger.intf;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. {$interfaces com}
  8. type
  9.   ILogger = interface
  10.     ['{F6A3D2F7-5901-463A-843F-6467F36D9B37}']
  11.     // With simple use, the guid is not necessary, but if you want to use "support", for example, you will get a message that there is no GUID.
  12.     // So always use a GUID.
  13.     function Log(const Msg: String): String;
  14.   end;
  15.  
  16. implementation
  17.  
  18. end.

Code: Pascal  [Select][+][-]
  1. unit logger.model;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, logger.intf;
  9.  
  10. type
  11.   { TLogger }
  12.   TLogger = class(TInterfacedObject, ILogger)
  13.   public
  14.     function Log(const Msg: String): String;
  15.   end;
  16.  
  17.   TSomeClass = class(TInterfacedObject, ILogger)
  18.   private
  19.     fLogger: TLogger;
  20.   public
  21.     property logger: TLogger read fLogger implements ILogger;  //<---------- here
  22.   end;
  23.  
  24. implementation
  25.  
  26. { TLogger }
  27. function TLogger.Log(const Msg: String): String;
  28. begin
  29.   Result:= DateTimeToStr(Now) + ' - ' + Msg;
  30. end;
  31.  
  32. end.

cdbc

  • Hero Member
  • *****
  • Posts: 2818
    • http://www.cdbc.dk
Re: When is it useful for a property to implement an interface?
« Reply #1 on: May 13, 2026, 09:34:10 pm »
Hi Hans
For example here:
Code: Pascal  [Select][+][-]
  1. procedure MainProc;
  2. var view: TfrmMain; cmd: ICmdLineParams = nil;
  3. begin
  4.   fpgApplication.Initialize;
  5.   if fpgApplication.GetInterface(ICmdLineParams,cmd) then begin
  6.     if cmd.HasOption('style') then fpgStyleManager.SetStyle(cmd.GetOptionValue('style'))
  7.     else fpgStyleManager.SetStyle('carbide-dark'); /// carbide  Plastic Dark fusion-dark
  8.   end;
  9.   view := TfrmMain.Create(nil);
  10.   fpgApplication.MainForm:= view;
  11.   try
  12.     view.LogParser:= CreLogParserRTV(true);
  13.     view.Show;
  14.     fpgApplication.Run;
  15.   finally
  16.     view.Free;
  17.   end;
  18. end;
The fpgApplication doesn't implement "ICmdLineParams", but exposes it as a property of TfpgApplication.
This means you can get the interface off of the object where it feels natural but implement it in another class, which again means the interface's refcount won't 'Free' the /Host-object/ prematurely  ;D
Very Handy with COM-Interfaces...
<edit>
You can even host it in the 'private' visibility section, as can be seen below:
Code: Pascal  [Select][+][-]
  1.   TfpgApplicationBase = class(TfpgComponent, ICmdLineParams)
  2.   private
  3.     FMainForm: TfpgWidgetBase;
  4.     FTerminated: boolean;
  5.     FCritSect: TCriticalSection;
  6.     FWakeChannel: IWakeChannel;
  7.     FHelpKey: word;
  8.     FHelpFile: TfpgString;
  9.     FCmdLineParams: ICmdLineParams;
  10.     FDesignedDPI: integer;
  11.     function    GetForm(Index: Integer): TfpgWidgetBase;
  12.     function    GetFormCount: integer;
  13.     function    GetTopModalForm: TfpgWidgetBase;
  14.     function    GetHelpFile: TfpgString;
  15.     function    GetCmdLineParamsInterface: ICmdLineParams;
  16.     property    CmdLineParams: ICmdLineParams read GetCmdLineParamsInterface implements ICmdLineParams;
  17.   protected  
  18.   ...
In the 'getter', Graeme thinks ahead and keeps the interface internally as an interface, thus it's ready if you need it again ...and being an interface, it manages its own lifetime, eventually when the application destroys, it goes out of scope and bye bye... Nifty right  8)
Code: Pascal  [Select][+][-]
  1. function TfpgApplicationBase.GetCmdLineParamsInterface: ICmdLineParams;
  2. begin
  3.   if not Assigned(FCmdLineParams) then
  4.     FCmdLineParams := TfpgCmdLineParams.Create;
  5.   Result := FCmdLineParams;
  6. end;
  7.      
</edit>
Regards Benny
« Last Edit: May 13, 2026, 09:59:02 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

Thaddy

  • Hero Member
  • *****
  • Posts: 19278
  • Glad to be alive.
Re: When is it useful for a property to implement an interface?
« Reply #2 on: May 14, 2026, 06:51:18 am »
A simpler example.
Suppose I am writing a piece of editing software and I will make it multi-platform, say Unix and Windows.
At a high level I simply write an Editor class, but I will use interface delegation to implement platform speciifics like clipboard handling etc. By using implements in my editor class I can delegate the platform specific code to implementors.
Something like this:
Code: Pascal  [Select][+][-]
  1. {$ifdef fpc}{$mode delphi}{$endif}
  2. type
  3.   IEditor = interface
  4.   ['{E51F1232-C8AA-472D-B2F8-E42A4D2C4678}']
  5.     procedure Edit;
  6.     procedure View;
  7.   end;
  8.  
  9.   TEditImplementor = class(TInterfacedObject,IEditor)
  10.     procedure Edit;
  11.     procedure View;
  12.   end;
  13.  
  14.   TEditor = class(TInterfacedObject, IEditor)
  15.   private
  16.     FImplementor: IEditor;
  17.   public
  18.     constructor create(const value:IEditor);virtual;
  19.     property Editor: IEditor read FImplementor implements IEditor;
  20.   end;
  21.  
  22. procedure TEditImplementor.Edit;
  23. begin
  24.   writeln('Edit');
  25. end;
  26.  
  27. procedure TEditImplementor.View;
  28. begin
  29.   writeln('View');
  30. end;
  31.    
  32. constructor TEditor.create(const value:IEditor);
  33. begin
  34.   inherited create;
  35.   FImplementor := value;
  36. end;
  37.  
  38. var
  39.   MyClass: TEditor;
  40.   MyInterface: IEditor;
  41. begin
  42.   MyClass := TEditor.Create(TEditImplementor.Create);
  43.   MyInterface := MyClass;
  44.   MyInterface.Edit;  // calls TEditImplementor.Edit
  45.   MyInterface.View;  // calls TEditImplementor.View
  46.   MyClass.Free;
  47. end.
This differs with normal implementations in that I do not need to implement the interface calls myself: the delegated class provides the implementation and everything else stays the same. E.g.: No inheritance necessary.
Under circumstances, but not always, this keeps the top class a bit cleaner. It is not often used, but quite powerful.
« Last Edit: May 14, 2026, 11:25:42 am by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

cdbc

  • Hero Member
  • *****
  • Posts: 2818
    • http://www.cdbc.dk
Re: When is it useful for a property to implement an interface?
« Reply #3 on: May 14, 2026, 12:05:05 pm »
Hi Thaddy
Nice  8)
That's also a pretty good example of "Dependency Injection", good on you mate =^  ;D
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

Hansvb

  • Hero Member
  • *****
  • Posts: 922
Re: When is it useful for a property to implement an interface?
« Reply #4 on: May 14, 2026, 02:18:50 pm »
Thanks. I can follow this
Code: Pascal  [Select][+][-]
  1.  I will use interface delegation to implement platform specifics like clipboard handling etc
and it is good to keep in mind

 

TinyPortal © 2005-2018