0
votes

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.

5
Well, you just shouldn't use Create because that is used for constructors and this is not a constructor.David Heffernan
>>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
@David Heffernan But Borland did it :(Ian Boyd
@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
I'd name those methods CreateInstance and be done with it.The_Fox

5 Answers

7
votes

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

4
votes

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.

3
votes

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.

1
votes

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

0
votes

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.