Articles Partially Reimplementing / Overriding Interface Implementation

FireWind

Свой
Регистрация
2 Дек 2005
Сообщения
1,957
Реакции
1,199
Credits
4,009
Partially Reimplementing / Overriding Interface Implementation
Žarko Gajić
[SHOWTOGROUPS=4,20]
Partially Reimplementing / Overriding Interface Implementation
Žarko Gajić

In Delphi, the reserved word “interface” has two distinct meanings. In a Delphi unit, “interface” denotes the start of a unit section used to declare public constants, data types, variables, procedures and functions visible and accessible to other units using this unit. In OOP jargon, an “object interface” or simply “interface” is a kind of a class with no implementation (but not like a class with abstract methods). An interface defines methods that can be implemented by a class (a class “implements” the interface). Object interfaces might be used when multiple inheritance is needed, and are frequently used when working with COM objects. For some intro to interfaces: Для просмотра ссылки Войди или Зарегистрируйся.
1589571146424.png

In recent days I’ve been playing with some COM objects (developed in C++, consuming in my Delphi application) exposing properties (and methods) of interface type.


In some pseudo code, that would look like:

Код:
anObject : ISomeObject; //has a property IProperty of IInterfaceProperty type
objProperty : IInterfaceProperty
...
objProperty := anObject.IProperty
objProperty.DoSomething;
//or
anObject.IProperty.DoSomething;

Some properties are declared as read/write – allowing the consumer (me) of the object to reimplement that property’s implementation. Since the property IProperty has a setter and is of IInterfaceProperty type – reimplementing what it does is rather simple: create a Delphi class implementing IInterfaceProperty and set it for the IProperty of the object. When creating my reimplementing class, store the reference to the original IProperty implementation and when there’s no need to override a particular method – just call the original method in my “overridden” method. Case closed


The “problem” I encountered is: IInterfaceProperty has tons of methods and my custom IInterfaceProperty implementing class must implement all of them (even if I would just call the original method implementation) – while I need to override only a few. Hence the idea for this post:

How to Partially Override Interface Implementation

Ok, let’s try to create a simple setup as above. (Hint: noting with interfaces is simple)
The IInterfaceProperty will be IMyInterface and defines a bunch of functions, for the sake of simplicity, something like:

Код:
type
  IMyInterface = interface(IInterface)
    function F1 : integer;
    function F2 : integer;
    //and many more...
    //function F3 : integer;
    //function F4 : integer;
    //function F5 : integer;
    //..
    //function FN : integer;
  end;

And then we have a class implementing that interface and exposing the interface implementation via a read/write property:

Код:
TMyClass = class(TInterfacedObject, IMyInterface)
strict private
  type TMyInterfaceImpl = class(TInterfacedObject, IMyInterface)
    function F1 : integer;
    function F2 : integer;
  end;
private
  fMyInterface: IMyInterface;
public
  property MyInterface : IMyInterface read fMyInterface write fMyInterface implements IMyInterface;
 
  function MinusF1 : integer;
 
  constructor Create();
end;

Here’s the implementation part:

Код:
constructor TMyClass.Create;
begin
  inherited;
 
  fMyInterface := TMyInterfaceImpl.Create;
end;
 
function TMyClass.MinusF1: integer;
begin
  result := -1 * self.MyInterface.F1;
end;

So, when the object of TMyClass is created the constructor assigns to fMyInterface an instance of a class actually internally implementing the interface methods:

Код:
function TMyClass.TMyInterfaceImpl.F1: integer;
begin
  result := 1;
end;
 
function TMyClass.TMyInterfaceImpl.F2: integer;
begin
  result := 2;
end;

If you look inside Delphi help, that’s called “implementing interfaces by delegation – delegating to an interface-type property”.

Ok, so far all set. Here’s a simple test to prove this all works:

Код:
var
  my : TMyClass;
begin
  my := TMyClass.Create;
 
  Writeln ('F1: ' + my.MyInterface.F1.ToString);
  Writeln ('F2: ' + my.MyInterface.F2.ToString);
  Writeln('-F1: ' + my.MinusF1.ToString);
end;

The output would be:



Now, what I want is to reimplement what my.MyInterface methods do. As stated above, the direct approach is to create a class implementing IMyInterface and assign it to my.MyInterface, like in:

Код:
TMyPlus100Class = class(TInterfacedObject, IMyInterface)
private
  fIOriginal: IMyInterface;
public
  constructor Create(const original : IMyInterface);
 
  //must implement all IMyInterface methods
  function F1: integer;
  function F2: integer;
//…
// function FN: integer;
end;

The implementation is whatever we want:

Код:
constructor TMyPlus100Class.Create(const original : IMyInterface);
begin
  self.fIOriginal := original;
end;
 
function TMyPlus100Class.F1: integer;
begin
  //add 100 to original result – so like override method implementation
  result := 100 + fIOriginal.F1;
end;
 
function TMyPlus100Class.F2: integer;
begin
  //just return original value
  result := fIOriginal.F2;
end;

To use my reimplementing class I would do:

Код:
my := TMyClass.Create;
 
my.MyInterface := TMyPlus100Class.Create(my);
 
Writeln ('Plus100 F1:' + my.MyInterface.F1.ToString);
Writeln ('Plus100 F2:' + my.MyInterface.F2.ToString);
Writeln ('Plus100 -F1: ' + my.MinusF1.ToString);

nd the output would be


Plus100 F1:101
Plus100 F2:2
Plus100 -F1: -101

Now, that’s all great and works as expected – however, I need to override just a few methods. My TMyPlus100Class must implement all functions F1 … FN while mostly all I will do is to return the original value and only for some methods I would return something else.


So, the idea is to have a reimplemnting class store the original implementation as a property by delegation and try to “override” just one method (say “F1):

Код:
  TMyPlus200Class = class(TInterfacedObject, IMyInterface)
  private
    fIOriginal: IMyInterface;
    property IOriginal : IMyInterface read fIOriginal implements IMyInterface;
  public
    constructor Create(const original : IMyInterface);
    
    //override just F1
    function F1: integer;
  end;
 
function TMyPlus200Class.F1: integer;
begin
  result := 200 + fIOriginal.F1;
end;

This looks ok, and compiles, but once used:

Код:
my := TMyClass.Create;
 
my.MyInterface := TMyPlus200Class.Create(my);
 
Writeln ('Plus200 F1:' + my.MyInterface.F1.ToString);
Writeln ('Plus200 F2:' + my.MyInterface.F2.ToString);
Writeln ('Plus200 -F1: ' + my.MinusF1.ToString);

he output would be


Plus200 F1:1
Plus200 F2:2
Plus200 -F1: -1

Nothing happened!! My TMyPlus200Class did nothing. That's actually to expect, as if you read the Help: If the delegate property is of a class type, that class and its ancestors are searched for methods implementing the specified interface before the enclosing class and its ancestors are searched.


If I try to use something called “method resolutions for interface”, like:

Код:
TMyPlus200Class = class(TInterfacedObject, IMyInterface)
private
  fIOriginal: IMyInterface;
  property IOriginal : IMyInterface read fIOriginal implements IMyInterface;
public
  constructor Create(const original : IMyInterface);
 
  function F1: integer;
 
  function IMyInterface.F1 = F1; //<- Please use my F1 as an override for  IOriginal.F1
end;

The above does not compile with : [dcc32 Error] E2264 Cannot have method resolutions for interface 'IMyInterface'


So, what now? Well, let’s introduce another class with a derived class and method resolution, as in:


Код:
TMyPlus300Class = class(TInterfacedObject, IMyInterface)
private
  fIOriginal: IMyInterface;
  property IOriginal : IMyInterface read fIOriginal implements IMyInterface;
public
  constructor Create(const original : IMyInterface);
  function _F1: integer;
end;
 
TMyPlus300ClassImplementator = class(TMyPlus300Class, IMyInterface)
public
  function IMyInterface.F1 = _F1;
end;

Note: IOriginal is a private property of TMyPlus300Class, TMyPlus300ClassImplementator extends TMyPlus300Class and uses method resolution to specify what method actually implements the IMyInterface's F1 method.


With implementation:


Код:
constructor TMyPlus300Class.Create(const original: IMyInterface);
begin
  self.fIOriginal := original;
end;
 
function TMyPlus300Class._F1: integer;
begin
  result := 300 + self.IOriginal.F1;
end;

When pushing my partial re-implementation I just need to make sure I create an instance of TMyPlus300ClassImplementator (and NOT TMyPlus300Class), as in:

Код:
my := TMyClass.Create;
 
my.MyInterface := TMyPlus300ClassImplementator.Create(my);
 
Writeln ('Plus300 F1:' + my.MyInterface.F1.ToString);
Writeln ('Plus300 F2:' + my.MyInterface.F2.ToString);
Writeln ('Plus300 -F1: ' + my.MinusF1.ToString);

And the output:


Plus300 F1:301
Plus300 F2:2
Plus300 -F1: -301

Voila! Just re-implemented the interface implementation partially: only one method returns a different result compared to original implementation.


p.s.
If you are still reading: bravo. I got lost of all the interface this implementation that


p.s.2
Disclaimer: I'm not freeing any objects above, yes I know.
[/SHOWTOGROUPS]