tags:

views:

165

answers:

6

Hi,

I want to limit the access of protected methods to certain inherited classes only.

For example there is a base class like

TBase = Class
  Protected
    Method1;
    Method2;
    Method3;
    Method4;
End;

I have two classes derived from TBase

TDerived1 = Class(TBase)
  //Here i must access only Method1,Method2 and Method3
End;

TDerived2 = Class(TBase)
  //Here i must access only Method3 and Method4
End;

Then is it possible to access only Method1, Method2 and Method3 when i use objects of TDerived1 and Method3 and Method4 when i use objects of TDerived2

+7  A: 

There's no way to do that. If a method is protected, then all descendant classes have access to it. You might want to rethink your class design if you have two separate sets of functionality that can be divided that easily.

Mason Wheeler
+1  A: 

A solution that works in a similar way to publish private/protected/public properties that works for methods.
So you could do it like this:

unit PropertyAndMethodVisibilityPromotionUnit;

interface

type
  TBase = class
  private
    procedure Method1;
    procedure Method2;
    procedure Method3;
    procedure Method4;
  end;

  TBase1 = class(TBase)
  protected
    procedure Method1;
    procedure Method2;
  end;

  TBase2 = class(TBase)
  protected
    procedure Method3;
    procedure Method4;
  end;

  TDerived1 = class(TBase1)
    //Here i must access only Method1 and Method2
  end;

  TDerived2 = class(TBase2)
    //Here i must access only Method3 and Method4
  end;

implementation

procedure TBase.Method1;
begin

end;

procedure TBase.Method2;
begin

end;

procedure TBase.Method3;
begin

end;

procedure TBase.Method4;
begin

end;

procedure TBase1.Method1;
begin
  inherited;
end;

procedure TBase1.Method2;
begin
  inherited;
end;

procedure TBase2.Method3;
begin
  inherited;
end;

procedure TBase2.Method4;
begin
  inherited;
end;

end.

Notes:

  1. This only works if TBase, TBase1 and TBase2 are in the same unit.
  2. It is a hack working around a potentially weak class design, so be sure you review your class design

--jeroen

Jeroen Pluimers
Do you suggest that just "redeclaring" a method in the class declaration promotes its visibility? This doesn't work for me in D2007 - I have to **implement** a forwarding method **and** the method has to be protected in the base class nevertheless.
Ulrich Gerhardt
That *kind of* work... If classes are declared in the same unit, you could use that method. Just have TBase1.Method1 call "inherited Method1". But it's pretty pointless to do so.Still bad design IMHO.
Ken Bourassa
@Ulrich: I edited the post with a more elaborate example. @Ken: Totally agreed, hence the notes I added in the edito.
Jeroen Pluimers
+3  A: 

I'd split them, similar to Jeroen's answer:

  TBase = class
  end;

  TBase12 = class(TBase)
  protected
    procedure Method1;
    procedure Method2;
  end;

  TBase34 = class(TBase)
  protected
    procedure Method3;
    procedure Method4;
  end;

  TDerived1 = class(TBase12)
  end;

  TDerived2 = class(TBase34)
  end;

From what you describe, this seems to better model your requirements than a "monolithic" base class (like Mason already wrote).

Ulrich Gerhardt
+1. This is exactly what I meant about rethinking the class design.
Mason Wheeler
You can perform this redesign only if Method1 and Method2 do not depend on Method3 and Method4 (and vice versa); hence I didn't redesign it :-)
Jeroen Pluimers
Yes, of course. The exact possibilties depend on the exact situation. :-)
Ulrich Gerhardt
+1  A: 

One more way - you can do this using Interfaces...

IBase1 = interface
  // press Ctrl+Shift+G here to generate your own sexy GUID
  procedure Method1;
  procedure Method2;
end;

IBase2 = interface
  // press Ctrl+Shift+G here again
  procedure Method3;
  procedure Method4;
end;

TBase = class(TInterfacedObject, IBase1, IBase2)
public
  { IBase1 }
  procedure Method1;
  procedure Method2;
  { IBase2 }
  procedure Method3;
  procedure Method4;
end;

var
  B1: IBase1;
  B2: IBase2;
begin
  B1 := TBase.Create as IBase1;
  B2 := TBase.Create as IBase2;

  B1.Method1; // works
  B1.Method3; // Can't compile

  B2.Method3; // works
end;
DiGi
+1  A: 

Seems to me your methods aren't declared in the right place.

If Method1 and Method2 are not called in TBase, and should only be called from TDerived1 and descendents... then those methods should be declared in TDerived1.

If Method1/2 access private fields of TBase, then you should have properties or Getter/setter to those field in TBase.

But unless you give more specific reasons as to why those methods need to be in declared in TBase, I'd say it's just bad design to declare them there.

Ken Bourassa
A: 

ok... Here's a possible way to achieve what you are looking for. I think it requires Delphi 2005 or later though. (Or whatever version that introduced the "Strict Protected|private" visibility)

TBase = Class
  Strict Protected
    procedure Method1;
    procedure Method2;
    procedure Method3;
    procedure Method4;
End;

TDerived1 = Class(TBase)
  Protected
    procedure Method1;
    procedure Method2;
    procedure Method3;   
End;

TDerived2 = Class(TBase)
  Protected
    procedure Method3;
    procedure Method4;
End;

TUserClass = class
  FImplementer : TDerived1;
end;

And the methods look like this

procedure TDerived2.Method3;
begin
  inherited Method3;
end;

But your requirements make me wonder if your method really belongs to your TBase class. Seems they should be static procedure, or maybe class procedure of another class. I don't think they really belong to TBase.

Ken Bourassa