IT SOLUTIONS
Your full service technology partner! 
-Collapse +Expand
Delphi
Search Delphi Group:

Advanced
-Collapse +Expand Delphi To/From
To/FromCODEGuides
-Collapse +Expand Delphi Store
PRESTWOODSTORE

Prestwood eMagazine

October Edition
Subscribe now! It's Free!
Enter your email:

   ► KBProgrammingDelphi for W...OOP   Print This     
  From the June 2015 Issue of Prestwood eMag
 
Delphi OOP:
Delphi Class Helpers (class helper for)
 
Posted 15 years ago on 2/7/2009 and updated 3/12/2010
Delphi Code Snippet:
 A flashcard from our Delphi Flashcards Library
 A code snippet from our Delphi Code Snippets Page

KB101869

General Info: Class Helper

A. In Dephi, class helpers allow you to extend a class without using inheritance. With a class helper, you do not have to create and use a new class descending from a class but instead you enhance the class directly and continue using it as you always have (even just with the DCU).

B. In general terms, developers sometimes use the term to refer to any class that helps out another class.

Delphi Class Helpers

Delphi allows you to extend an existing class without using inheritance. Buggy in 2005 and not officially supported but stable, usable, and officially supported in 2006 and above.

You declare a class helper similiar to how you declare a class but use the keywords class helper for.

Syntax Example:
TCyborg = class(TObject)
public
  FCyborgName: String;
end;
  
TCyborgHelper = class helper for TCyborg
  procedure ShowClassName;
end;

Details

  • You can name a helper anything but it is customary to use the class name and add "Helper". For example, you might name a TObject helper class TObjectHelper.
  • Only one helper (or none) applies to a class at any given time. If you choose to implement more than one class helper per project (per compile), the nearest class helper is used (based on your uses clause).
     
    Note: Although you can define more than one helper, I recommend you limit yourself to either none or one per class per project. To help make maintenance and enhancements easier, you might consider having one unit perhaps called helpers.pas per project. I suggest you think of class helpers on a per project basis and avoid creating reusable class helpers that span multiple projects. If you desire to have class helpers for multiple projects, you might consider having a library of class helpers available for use.
     
    The use of class helpers already adds ambiguity so I actually hope the Delphi compiler team restricts class helpers to none or one per class per compile in a future version of Delphi. Thus enforcing one helper per class per project. If that's not possible, I'd like them to remove the ambiguity of multiple class helpers and simply bring in all class helpers and surface errors for any conflicts. If that never happens, or until then, I suggest adopting a one class helper per class per project standard.
     
  • You do not need the class source code (the .DCU will work just fine).
  • Helpers have access only to public members of the class it is helping. Helpers do not have access to private nor protected members of the class they are helping.
  • You can introduce new member visibility such as private and protected (strict private and strict protected too). If you don't specify, all new members are public.
  • You cannot create an object instance directly from a class helper.
  • self refers to the class being helped.
  • The routines added by a class helper can be used by the class and descendant classes.
  • You can add a procedure, function, or property but you cannot add a member field. This means helper properties must make use of existing member fields.

Uses for Class Helpers

Like any language feature, class helpers are powerful in the right hands but you should take care not to overuse them. When you add a class helper, encapsulation is now a bit more ambiguous. A class helper breaks, or at least cracks, OO approach to coding but is sometimes the best way to accomplish a task.

Many developers disagree with the following statement in Delphi's help, "[class helpers]...should not be viewed as a design tool to be used when developing new code. They should be used solely for their intended purpose, which is language and platform RTL binding". Perhaps the driving source of that statement is from a pure OO approach to development where a class implements all functionality discovered during requirements and specified during design. The problem is especially clear with code libraries such as the VCL where it is difficult to predict and implement all future uses. When designing a rich code library, you have to decide when to surface what functionality, which classes a user can inherit from, when to seal a class, etc. Sometimes, allowing a user to use class helpers to add their routines to your code library is the best approach. Whether you use them within you're own code design for an application or your company's code library is a bit more questionable but ultimately if the final code is more maintainable and easier to extend, then you've done your job. 

The Good:

  • Do use a class helper when you do not own the class you wish to extend and there isn't a more appropriate class to inherit from. For example, many developers like to use a class helper when they wish to add a few routines to an existing VCL class.
  • Many developers like to use class helpers for those little routines that don't really belong to a class but are associated with the class. Some developers say those should be global functions and procedures or class members of a utility class while others say they should be part of a class helper.

The Bad and the Ugly:

  • Do not use a class helper when inheritance is a better choice.
  • Do not use a class helper when you wish to extend a class and you own the source code. Just extend the class directly.

Class Helpers versus Partial Classes

Like partial classes, class helpers are primarily a source code control feature. A class helper tells the compiler to increase the compile scope when resolving identifiers. In very general terms, class helpers allow you to easily extend a class when inheritance isn't possible or practical. Contrast with partial classes which are used for source code management with code generators and large classes to split ownership of a class. Class helpers allow you to cheat a bit by enhancing an existing class without altering it while partial classes allow you to divide your source code and conquer.

Note: Delphi doesn't currently support partial classes. Delphi Prism and most other .Net languages do such as C# and VB.Net (C++/CLI does not). Although both class helpers and partial classes are controversial, I hope a future version of Delphi will support both. I think there is a place for both class helpers and partial classes in OO languages. In the right hands, I think both features lend themselves to more elegant code that is easier to maintain and enhance.

Delphi 2009 Working Example

The following example demonstrates adding a procedure using a helper class to our own TCyborg class. The name of the helper class is TCyborgHelper.

Create a Windows form application and place two buttons on it. Alter the code as follows:

//Add this class to the interface section:
TCyborg = class(TObject)
public
  FCyborgName: String;
end;
  
//Add this class helper to the interface section too:
TCyborgHelper = class helper for TCyborg
  procedure ShowClassName;
end;
  
//Add the procedure's code to the implementation section:
procedure TCyborgHelper.ShowClassName;
begin
  ShowMessage(Self.ClassName);
end;

 

You can exercise our new class and class helper with the following code. For example, on the click events of two buttons.

//Normal usage using a class member field.
var
  MyRobot: TCyborg;
begin
  MyRobot := TCyborg.Create;
  MyRobot.FCyborgName := 'Cameron';
  ShowMessage('Hi, my name is ' + MyRobot.FCyborgName);
  FreeAndNil(MyRobot);
end;
  
//Use helper procedure. Notice self refers to the 
//TCyborg class and not to the TCyborgHelper class.
var
  MyRobot: TCyborg;
begin
  MyRobot := TCyborg.Create;
  MyRobot.ShowClassName;
  FreeAndNil(MyRobot);
end;

As you can see by the above simple demo, adding a routine to an existing class using a class helper is very easy and you use the original class as you always have but now have access to the new routines you've added.

A Simple Delphi 2009 VCL Example

The following code adds a simple procedure to the VCL TEdit class. With class helpers, you do not have to create and use a new class descending from TCustomEdit (perhaps called TMyEdit) but instead you enhance TEdit directly and continue using it as you always have.

unit EditHelper;
  
interface
uses
  StdCtrls, Dialogs;
  
type
  TEditHelper = class helper for TEdit
    procedure ShowClassName;
  end;
  
implementation
  
procedure TEditHelper.ShowClassName;
begin
  //Self refers to the helped class.
  //Self here refers to TEdit.
  ShowMessage(Self.ClassName);
end;
  
end.

 

Now, in a form with a TEdit control named Edit1 and a button, you can make use of our newly added TEdit functionality. For example, alter the click event of a button as follows:

procedure TForm1.Button3Click(Sender: TObject);
begin
  Edit1.ShowClassName;
end;

Now that's powerful and, when done right, it's easy to implement, maintain, and extend.

Add Properties too!

You can add properties to an existing class using a class helper too. You cannot add a new member field so your new property must make use of existing member fields.

For my last example, we will enhance our Cyborg class. Currently the Cyborg class uses a public member field named FCyborgName to store the cyborg's name. This really should be a private member field accessed through a property. Our first choice is to rewrite the class and correct the less than elegant code. However, let's assume we do not own the code so a class helper is our only approach.

The following code adds a public property called CyborgName to our existing class:

//Alter class helper as follows: 
CyborgHelper = class helper for TCyborg
strict private
  function GetCyborgName: String;
  procedure SetCyborgName(const pValue: String);

public
  property CyborgName: String read GetCyborgName write SetCyborgName;
  procedure ShowClassName;
end;
  
//Implement new getter and setter in interface section:
function TCyborgHelper.GetCyborgName: String;
begin
  Result := FCyborgName;
end;
  
procedure TCyborgHelper.SetCyborgName(const pValue: String);
begin
  FCyborgName := pValue;
end;

 

Now you can use either the public FCyborgName member field or our new CyborgName property. Not as elegant as rewriting the original class and correcting the problem, but this code does demonstrate adding a new property to an existing class.

procedure TForm1.Button5Click(Sender: TObject);
var
  MyRobot: TCyborg;
begin
  MyRobot := TCyborg.Create;
  MyRobot.CyborgName := 'Cameron';
  ShowMessage('Hi, my name is ' + MyRobot.CyborgName);
  FreeAndNil(MyRobot);
end;

 

More Info

Definition:  Class Helper
Definition:  Partial Class

Comments

0 Comments.
Share a thought or comment...
 
Write a Comment...
...
Sign in...

If you are a member, Sign In. Or, you can Create a Free account now.


Anonymous Post (text-only, no HTML):

Enter your name and security key.

Your Name:
Security key = P146A1
Enter key:
Code Contributed By Mike Prestwood:

Mike Prestwood is a drummer, an author, and creator of the PrestwoodBoards online community. He is the President & CEO of Prestwood IT Solutions. Prestwood IT provides Coding, Website, and Computer Tech services. Mike has authored 6 computer books and over 1,200 articles. As a drummer, he maintains play-drums.com and has authored 3 drum books. If you have a project you wish to discuss with Mike, you can send him a private message through his PrestwoodBoards home page or call him 9AM to 4PM PST at 916-726-5675 x205.

Visit Profile


Linked Certification Question(s)

The following are practice certification questions with answers highlighted. These questions were prepared by Mike Prestwood and are intended to stress an important aspect of this KB post. All our practice questions are intended to prepare you generally for passing any certification test as well as prepare you for professional work.

Advanced

1 Advanced Level Question

Question #1: Yes or No?

Will the following code compile?

TObjectHelper = class helper for TObject
strict private
  FTempStr: String;
public
  property TempStr: String
read FTempStr write FTempStr;
end;
Answer:
  • Yes
  • No

  •  KB Article #101869 Counter
    30498
    Since 2/7/2009
    Go ahead!   Use Us! Call: 916-726-5675  Or visit our new sales site: 
    www.prestwood.com


    ©1995-2024 Prestwood IT Solutions.   [Security & Privacy]