How to implement identical methods with 2 and more Classes?
Asked Answered
A

2

6

I want to write a TCheckBox and TRadioButton descendants having 3 identical methods.

TMyCheckBox = class(TCheckBox)
  procedure DoSomething1;
  procedure DoSomething2;
  procedure WMSize(var Message: TWMSize); message WM_SIZE;
end;

TMyRadioButton = class(TRadioButton)
  procedure DoSomething1;
  procedure DoSomething2;
  procedure WMSize(var Message: TWMSize); message WM_SIZE;
end;

// the following procedures are common for both classes, so in fact
// TMyCheckBox.DoSomething1 do the same as TMyRadioButton.DoSomething1

procedure DoSomething1;
begin
  // here is the same code for TMyCheckBox as well as for TMyRadioButton
  // but I don't want to write the same code many times but implement it
  // for both classes at once in some common way
end;

procedure DoSomething2;
begin
  // here is the same code for TMyCheckBox as well as for TMyRadioButton
  // but I don't want to write the same code many times but implement it
  // for both classes at once in some common way
end;

procedure WMSize(var Message: TWMSize); message WM_SIZE;
begin
  // here is the same code for TMyCheckBox as well as for TMyRadioButton
  // but I don't want to write the same code many times but implement it
  // for both classes at once in some common way
end;

How can I do this?

Atterbury answered 10/1, 2012 at 14:9 Comment(3)
Do you mean identical declarations (using Interface) or identical implementations (using same ancestor) ?Iambus
@Krom, That is a good question. What I actually meant is identical implementations.Atterbury
The you have to show us the implementation to get a valid answer.Columelliform
T
11

Define an interface say IDoSomething with the the three method signatures.

Then change your class declaration to

TMyCheckBox = class(TCheckBox, IDoSomething)

and then implement.

If the implementations are common or very close.

Then define a helper class TDoSomething and then delegate the work.

e.g.

Procedure TMyCheckBox.DoSomething1; // implements IDoSomething1
Begin
  TDoSomething.DoSomething1(Self); // given class method will suffice.
End;

Class Methods in delphi, equivalent to static methods in other languages.

Type
    TDoSomethingHelper = Class(TObject)
    Public
      Class Procedure DoSomething1(aComponent : TComponent);
    End;

...
implementation

Class Procedure TDoSomethingHelper.DoSomething1(aComponent : TComponent);
Begin
  aComponent.Tag = 27;
End;
Trinh answered 10/1, 2012 at 14:31 Comment(4)
How can I define such helper class in D7? is the DoSomething1 supposed to be a class method?Atterbury
what do I gain from the static method if I can create a procedure such as: Procedure DoSomething1(aComponent : TComponent); which is exactly what I'm doing now in my project?Atterbury
@user similar to source units, helper classes keep related helper methods together in one logical group, but you are right the difference is not bigLatin
Using an interface means you can pass references to instances of either component as IDoSomething. Using a helper class static or other wise means you implement once and delegate twice. Couldn't go any further without knowing what the 'common' methods do, and how you are planning on calling them. Not presenting a this is the only way to do it, just some language constructs that you could use to do this sort of thing.Trinh
K
8

You are looking for implementation inheritance rather than interface inheritance. This is only achievable in Delphi if you can derive classes from a single common ancestor. This limitation is inherent because the language only supports single-inheritance.

The best you can do is something like this:

type
  TMyWinControlExtender = class
  private
    FTarget: TWinControl;
  public
    constructor Create(Target: TWinControl);
    procedure WMSize(var Message: TWMSize; out CallInherited: Boolean);
    procedure DoSomething;
  end;

  TMyCheckBox = class(TCheckBox)
  private
    FExtender: TMyWinControlExtender;
  protected
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure DoSomething;
  end;

  TMyRadioButton = class(TRadioButton)
  private
    FExtender: TMyWinControlExtender;
  protected
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure DoSomething;
  end;

{ TMyWinControlExtender }

constructor TMyWinControlExtender.Create(Target: TWinControl);
begin
  inherited Create;
  FTarget := Target;
end;

procedure TMyWinControlExtender.WMSize(var Message: TWMSize; out CallInherited: Boolean);
begin
  if FTarget.... then
    ....
  CallInherited := ...;
  //etc.
end;

procedure TMyWinControlExtender.DoSomething;
begin
  if FTarget.... then
    ....
  //etc.
end;

{ TMyCheckBox }

constructor TMyCheckBox.Create(AOwner: TComponent);
begin
  inherited;
  FExtender := TMyWinControlExtender.Create(Self);
end;

destructor TMyCheckBox.Destroy;
begin
  FExtender.Free;
  inherited;
end;

procedure TMyCheckBox.DoSomething;
begin
  FExtender.DoSomething;
end;

procedure TMyCheckBox.WMSize(var Message: TWMSize);
var
  CallInherited: Boolean;
begin
  FExtender.WMSize(Message, CallInherited);
  if CallInherited then
    inherited;
end;

And likewise for TMyRadioButton etc.

Now, you could use interfaces and delegation to reduce some of the boilerplate, but there's no way for that to help with a message handler like WMSize.

Karylkarylin answered 10/1, 2012 at 17:31 Comment(2)
10x David. this code looks really good, specially how you handle the WMSize. It can be done with simple class methods/or TProcedure (passing Self as ref) more or less what I'm doing today. what I was hoping for was "multi-inheritance" (using Interfaces), but now I understand there is no magic here.Atterbury
Yes indeed. No multiple inheritance of implementation in Delphi. Few languages support that and in fact it often causes more trouble than it's worth.Karylkarylin

© 2022 - 2024 — McMap. All rights reserved.