0

This is a variation on my previous question on how to hide inherited constructors. How do you hide inherited methods:

Modelling after the way Delphi lets you construct COM objects:

CoDOMDocument = class
   class function Create: IXMLDOMDocument2;
end;

i have a factory that creates an object that implements an interface:

CoCondition = class
public
   class function Create: ICondition;
end;

This works fine and great. It works fine, even though there's a method in the ancestor called Create. It works because i don't have the overload keyword present. As soon as i add the overload keyword: Delphi will allow the inherited Create method to "shine through":

CoCondition = class
public
   class function Create: ICondition; overload;
end;

So now CoCondition has two Create method available:

class function CoCondition.Create: ICondition;
constructor TObject.Create;

And it's ambiguous over which one you want to call. The fix, obviously is to just not have the overload keyword (Why would you, you're not overloading anything?). Well it turns out i am overloading something:

CoCondition = class
public
   class function Create: ICondition; overload;
   class function Create(const ConditionType: TConditionType): ICondition; overload;
   class function Create(const PropertyName: string; const Operation: TConditionOperation; const Value: Variant): ICondition; overload;
   class function Create(const ConditionType: TConditionType; const SubConditions: IInterfaceList): ICondition; overload;
end;

Since i have the overload keyword, the class has five overloads, rather than just the four i want:

class function CoCondition.Create: ICondition;
class function CoCondition.Create(const ConditionType: TConditionType): ICondition; overload;
class function CoCondition.Create(const PropertyName: string; const Operation: TConditionOperation; const Value: Variant): ICondition; overload;
class function CoCondition.Create(const ConditionType: TConditionType; const SubConditions: IInterfaceList): ICondition; overload;
constructor TObject.Create;

i only want my four overloads to be present, and no others. i.e. i want to hide any ancestor methods.

How do i hide ancestor methods?


i also tried explicitely declaring the ancestor method, but having it in protected, so nobody can get at it:

CoCondition = class
protected
    constructor Create; overload;
public
    class function Create(): ICondition; overload;
    class function Create(const ConditionType: TConditionType): ICondition; overload;
    class function Create(const PropertyName: string; const Operation: TConditionOperation; const Value: Variant): ICondition; overload; //leaf
    class function Create(const ConditionType: TConditionType; const SubConditions: IInterfaceList): ICondition; overload; //AND/OR/NOT children
end;

But that doesn't compile because of ambigious overload of unparametered Create().


i also considered:

CoCondition = class
public
   class function Make(): ICondition; overload;
   class function Make(const ConditionType: TConditionType): ICondition; overload;
   class function Make(const PropertyName: string; const Operation: TConditionOperation; const Value: Variant): ICondition; overload; //leaf
   class function Make(const ConditionType: TConditionType; const SubConditions: IInterfaceList): ICondition; overload; //AND/OR/NOT children
end;

But rejected it.


i could just expose the object that implements the object:

TCondition = class(TInterfacedObject, ICondition)
...
public
  constructor Create; overload;
  constructor Create(const ConditionType: TConditionType); overload;
  constructor Create(const PropertyName: string; const Operation: TConditionOperation; const Value: Variant); overload; //leaf
  constructor Create(const ConditionType: TConditionType; const SubConditions: IInterfaceList); overload; //AND/OR/NOT children
end;

But i thought all the cool kids hide their objects.

Community
  • 1
  • 1
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
  • Well, you just shouldn't use Create because that is used for constructors and this is not a constructor. – David Heffernan Jan 20 '11 at 16:41
  • 2
    >>But i thought all the cool kids hide their objects.<< No, not in this example at least. All the kids who believe that this would make them cool try it -- but they cannot. As soon as they discover that, they WOSH! become cool kids. – TheBlastOne Jan 20 '11 at 17:07
  • @David Heffernan But Borland did it :( – Ian Boyd Jan 20 '11 at 18:11
  • @Ian Of course the code that you are quoting from Embarcadero was probably produced by the typelib importer. In any case, they aren't trying to overload. Anyway, I maintain that Create should be reserved for constructors. – David Heffernan Jan 20 '11 at 18:57
  • 3
    I'd name those methods `CreateInstance` and be done with it. – The_Fox Jan 21 '11 at 07:44
  • Why don't you REALLY hide things, and use the "HAS A FOO" instead of "IS A FOO" style? Why do you really need to use inheritance here, instead of having BAR inherit from FOO, put an instance of FOO inside BAR, and make it private. – Warren P Jan 21 '11 at 20:15
  • Because the `foo` is `Create`, which i cannot hide. – Ian Boyd Jan 24 '11 at 14:09

5 Answers5

7

Hiding methods is not possible, because it goes against the fundamentals of object oriented programming.

Even if a language would support hiding, you could always work around it.

If for instance, if you create a TAnimal class with a Name property, and then you create a TNamelessAnimal class where you want to hide the Name property.
Now then you could cast a TNamelessAnimal instance into a TAnimal reference, and still access the Name property.
This is perfectly logical, since a TNamelessAnimal, is a TAnimal, and therefore has the Name property.

--jeroen

Jeroen Wiert Pluimers
  • 23,965
  • 9
  • 74
  • 154
  • Well, you *can* hide ancestor methods; just not when you use `overload`. But the answer **can't be done** is probably as correct as it's going to get. – Ian Boyd Jan 20 '11 at 18:09
  • Since these are class methods, this argument does not apply. There are no instances until after the class function has run. – David Heffernan Jan 20 '11 at 18:58
  • 1
    It does; just cast put the class reference in a reference of the ancestor and you are done. For instance when `Name` is a class property, then `TAnimalClass = class of TAnimal; AnimalClass: TAnimalClass; AnimalClass := TNamelessAnimal; Writeln(AnimalClass.Name);` will access the class property. Same for class methods. – Jeroen Wiert Pluimers Jan 20 '11 at 19:03
  • @Jeroen You would never write code like that. You most commonly use `class of` when you are using virtual constructors. – David Heffernan Jan 20 '11 at 19:28
  • @David: I know, but it is the trick to work around 'hiding' methods if the language would allow for such 'hiding'. – Jeroen Wiert Pluimers Jan 20 '11 at 20:09
  • 2
    @David et Jeroen: any code that takes advantage of the metaclasses, polymorphic constructors and virtual class methods can end up doing things like your "trick" as a matter of course. – Eric Grange Jan 21 '11 at 05:30
4

Hiding a method is not supported in Delphi simply because it's not logical. Suppose ancestor TAncestor has public method AMethod. Now declare TDescendant=class(TAncestor) and have it override AMethod as protected. Now the user can simply cast your TDescendant to TAncestor and access the method, which was supposed to be hidden. I don't know if any object-oriented language supports hiding methods in ancestors, but I doubt there are any.

Eugene Mayevski 'Callback
  • 45,135
  • 8
  • 71
  • 121
  • "can simply case" should be "can simply _cast_", I think? – Larry Lustig Jan 20 '11 at 16:52
  • Since these are class methods, this argument does not apply. There are no instances until after the class function has run. – David Heffernan Jan 20 '11 at 18:59
  • @David so what? There's a class itself, which can be referenced directly. – Eugene Mayevski 'Callback Jan 20 '11 at 19:24
  • "I don't know if any object-oriented language supports hiding methods in ancestors, but I doubt there are any." Delphi supports hiding of methods in ancestors. You just declare a method with the same name, and hey presto!, you've hidden a method. – David Heffernan Jan 20 '11 at 19:39
  • @David no, you are wrong. Just tested in Delphi 5 - casting to ancestor successfully defeats any tricks with methods with the same name in descendant. – Eugene Mayevski 'Callback Jan 20 '11 at 19:49
  • @Eugene http://docwiki.embarcadero.com/RADStudio/en/Methods#Overriding_versus_Hiding – David Heffernan Jan 20 '11 at 19:59
  • @David I don't have time to try to guess what you meant, yet I repeat - take the compiler and check yourself. I did test different variants. – Eugene Mayevski 'Callback Jan 20 '11 at 20:02
  • @Eugene I understand that hidden methods can be found. The link is to the Delphi documentation which explicitly discusses the topic of hiding methods. I do agree with you and Jeroen though that hiding is generally bad and in fact I already up-voted you both earlier today, so I'm not really disagreeing with the main thrust of either of your answers! – David Heffernan Jan 20 '11 at 20:09
  • @David - In the example code that you linked, the method with the same name of the *ancestor* is called, IOW the hidden one. Not quite what you mean here I guess.. – Sertac Akyuz Jan 20 '11 at 20:59
  • @Sertac It illustrates hiding and exactly the issues all discussed here. Yes you can hide methods, but they can still be found if you "hunt" for them. – David Heffernan Jan 20 '11 at 21:07
  • @David - The example illustrates *hiding*, not *hunting*. And yet, the ancestor method is called as a consequence of hiding in the example. In fact, hiding is not about blocking access to ancestor's methods at all.. – Sertac Akyuz Jan 20 '11 at 21:26
3

My best way to 'hide' ancestor methods is to declare an interface with the names of the methods that matter to you...eg:

IMyInterface = interface
  procedure One;
  procedure Two;
end;

Then, implement these methods in your class, exposing your class as implementing IMyInterface eg:

TMyClass = class( TInterfacedObject, IMyInterface )
PUBLIC
  procedure One;
  procedure Two;
end;

Elsewhere in your code, pass around the instance of your class as an IMyInterface (not TMyClass) instead. This neatly hides ALL internals in your class and breaks up your code into nicely partioned modules.

You will be amazed how the interface declaration can be made to 'look' like a class i.e with properties too, for example this is legal and highly useful:

IMyInterface = interface
  function GetSomething : integer;
  procedure SetSomething( AValue : integer );
  property  Something : integer; read GetSomething write SetSomething;
end;

Once you start using interfaces it will be hard to stop.

Brian Frost
  • 13,334
  • 11
  • 80
  • 154
1

Do not bother to fight over it. Just put all your implementing methods and properties into protected scope of its own unit hence it will force you to use interface. Other choice is use different name for other Create methods with param(s) and remove the overload keyword

Cheers

APZ28
  • 997
  • 5
  • 4
0

Mark your new "version" of the named method with the "reintroduce" directive:

CoCondition = class
public
   class function Create: ICondition; reintroduce;
end;

This allows you to "reintroduce" a method name with different parameters.

But note that reintroducing is not the same as overloading (a virtual method)... if you invoke these methods using a class reference the Create method you end up invoking will depend on the specific type of the class reference involved, rather than invoking the "most derived version".

Note also that you cannot reduce the declared visibility of an inherited method... if there is a "public" Create method inherited, then reintroducing a "protected" Create method won't actually hide the public one at all.

Deltics
  • 22,162
  • 2
  • 42
  • 70