Amazon.com Widgets Accessing Private Stuff with Interfaces

Accessing Private Stuff with Interfaces

By Nick at June 27, 2011 17:38
Filed Under: Delphi, Tech Stuff

I have to admit that the following code surprised me.  I guess it makes sense, but I wouldn’t have guessed that you can expose private functionality via an interface like this.  Interesting. 

program DoPrivateStuff;

{$APPTYPE CONSOLE}

uses
  SysUtils;


type

  ITestInterface = interface
    procedure DoThisPrivateThing;
    procedure DoThisPublicThing;
  end;

  TTestClass = class(TInterfacedObject, ITestInterface)
  private
    procedure DoThisPrivateThing;
  public
    procedure DoThisPublicThing;
  end;

{ TTestClass }

procedure TTestClass.DoThisPublicThing;
begin
  Writeln('Doing a public thing');
end;

procedure TTestClass.DoThisPrivateThing;
begin
  WriteLn('Doing a private thing');
end;

var
   Test: ITestInterface;

begin
  try
    Test := TTestClass.Create;
    Test.DoThisPrivateThing;
    Test.DoThisPublicThing;
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Comments (12) -

6/27/2011 6:04:44 PM #

While it's interesting that it actually works it doesn't make any sense to me doing something like this.
I have seen code like this before in more complex classes and it was very confusing seeing private methods that are not used anywhere. The same is the case when you have a property defined in the interface with the set and get method but not having it on the class itself which just implements the set and get method. From just looking at the class itself (without checking the implemented interface) you don't get a clue those private methods are exposed somewhere.
This has a bad taste in my opinion.

Stefan Glienke Germany |

6/27/2011 6:06:42 PM #

That's just a side effect of Delphi's implementation of private.  See if strict private works.

Iman United States |

6/27/2011 6:21:46 PM #

Why does this surprise you ?

In your class you declare a private method, but an interface, by definition, has no concept of "private" and there is very deliberately no tight coupling of an interface to it's implementation class (other than in terms of the contract specified in the interface itself - you must provide this method).

So the method remains private if using a class reference, but if you obtain/are given an interface reference to that object then you of course have access to the methods in that interface, independent of any visibility specified for those methods in the implementation class (potentially/theoretically in some language that has no concept of visibility at all!).


It's no surprise that the implementation details of a class are not necessarily bound to/defined by the contents of any interface that it implements.


It is - or should be - no more surprising than the fact that a class may have a method that implements some method in an interface that has a completely difference name (using a method resolution clause):

  IMyInterface = interface
    function Foo: Boolean;
  end;


  TMyImpl = class(TInterfacedObject, IMyInterface)
  public
    function Bar: Boolean;
    function IMyInterface.Foo = Bar;
  end;


NOTE: Although it doesn't surprise me that a private method can be exposed via an interface, a convention I use in my own code is that where-ever possible, property getter/setter methods to satisfy an interface are declared "protected" and all other methods and properties are declared "public".

Jolyon Smith New Zealand |

6/27/2011 8:09:36 PM #

@Iman:  There is no need to try anything to see if it "works" - there is nothing here that is *not* working.

@Stefan:  The "bad taste" comes from the fact that any given interface is not necessarily bound to any one class, so expecting to be able to discern information about a class declaration from the declaration of an interface it implements (or vice versa) is to miss the point of interfaces entirely.

All you can do is document your specific implementation of an interface adequately.

I do this myself by laying out the class along these lines:

TMyClass = (TInterfacedObject, IFirstInterface
                               ISecondInterface)
// Non interfaced implementation details
private
protected
public

// Interface related members:

protected // IFirstInterface --------------
// property getter/setter methods
public
// methods and properties

protected //ISecondInterface --------------
public
  // etc

protected // Method Resolution Clauses (if required)
end;


When the stock ingredients leave a bad taste, feel free to add whatever seasonings make the dish palatable.  Smile

Jolyon Smith New Zealand |

6/27/2011 8:27:24 PM #

On the same line of thought exposed by Jolyon, it is perfectly legal (if not sane) to write stuff like:

program DoPrivateStuff;
{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  ITestInterface = interface
    procedure DoThisPrivateThing;
    procedure DoThisPublicThing;
  end;

  TTestClass = class(TInterfacedObject, ITestInterface)
  strict private
    procedure DoThisPrivateThing;
    procedure ITestInterface.DoThisPublicThing = DoThisPrivateThing;
  public
    procedure DoThisPublicThing;
    procedure ITestInterface.DoThisPrivateThing = DoThisPublicThing;
  end;

{ TTestClass }

procedure TTestClass.DoThisPublicThing;
begin
  Writeln('Doing a public thing');
end;

procedure TTestClass.DoThisPrivateThing;
begin
  WriteLn('Doing a private thing');
end;

var
   Test: ITestInterface;

begin
  try
    Test := TTestClass.Create;
    Write('calling Test.DoThisPrivateThing=');
    Test.DoThisPrivateThing;
    Write('calling Test.DoThisPublicThing=');
    Test.DoThisPublicThing;
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.


And the result:
calling Test.DoThisPrivateThing=Doing a public thing
calling Test.DoThisPublicThing=Doing a private thing


François United States |

6/27/2011 8:38:57 PM #

I thought this was pretty normal.  When I'm building a class that implements interfaces and is supposed to be used as an interface and not a class, I put the implementing methods in a private section and label it with a comment like //implementation of IWhatever.

That way, you get encapsulation of those methods, and you can know that they won't be called from external code unless it's using the interface reference.

Mason Wheeler United States |

6/27/2011 9:05:38 PM #

I ALWAYS implement the interface methods as private/protected methods in Delphi. I like doing this way to enforce the use of the interface instead of the class instance which I find it quite uneasy when C# force me to implement interface methods as public Tong

william Hong Kong S.A.R. |

6/28/2011 1:38:16 AM #

Interface-implementing methods that are private can be incredibly annoying if you're wanting to both descend from the class and tweak the inherited implementation - the VCL's implementation of IOleForm comes to mind. IMO, protected or strict protected visibility should always be used when implementing an interface, assuming the methods aren't appropriately public in the class.

Chris United Kingdom |

6/28/2011 1:00:31 PM #

While that is a clever idea for testing, the issue is that you have to descend from TInterfacedObject or implement reference counting yourself (adding clutter).

The other is that a class instance and an interface reference don't behave the same way, which might cause a test to work, when it wouldn't work with a class instance or vice versa (reference counting vs. explicit freeing).

Maël Hörz Germany |

6/29/2011 10:16:51 AM #

Jolyon -

It surprises me because I am not as smart as you are.  Wink

And I agree with Stefan that it doesn't "feel" right.

nick United States |

6/30/2011 4:55:01 AM #

I agree with Mason. When you consider that the conventional advice has been to avoid mixing object and interface references to the same instance classes with no public members make more sense.

Lachlan Gemmell Australia |

7/1/2011 3:25:10 AM #

BTW: this isn't exactly an "old" feature. in D2007 interface methods still need to be either protected or public. So we always had a lot of "protected" stuff that actually belonged to the private sector...

Daniel Bernhard Switzerland |

Comments are closed

My Book

A Pithy Quote for You

"There's a fine line between fishing and just standing on the shore like an idiot."    –  Steve Wright

Amazon Gift Cards

General Disclaimer

The views I express here are entirely my own and not necessarily those of any other rational person or organization.  However, I strongly recommend that you agree with pretty much everything I say because, well, I'm right.  Most of the time. Except when I'm not, in which case, you shouldn't agree with me.

Month List