Home > Delphi, Did You Know > Class Helper

Class Helper


Pada post blog sebelumnya, saya membahas mengenai beberapa hal baru pada Delphi sejak Delphi 7, dalam artian berbagai perbaikan yang diusung oleh Delphi 2005, Delphi 2006 dan Delphi 2007, baik dari sisi bahasa pemrograman ObjectPascal, fitur IDE maupun perbaikan pada VCL (Visual Component Library).

Nah pada tulisan kali ini saya akan sedikit membahas mengenai salah satu fitur baru pada sisi bahasa ObjectPascal, yaitu mengenai Class Helper. Saya akan coba bahas implementasi Class Helper, walaupun telah disederhanakan untuk memudahkan dalam hal pemahaman materi.

Apa Itu Class Helper

Secara singkat, Class Helper adalah suatu kelas yang digunakan untuk menambahkan method dan properti kelas lainnya tanpa harus membuat kelas turunan untuk menambahkannya.

A class helper is a type that – when associated with another class – introduces additional method names and properties which may be used in the context of the associated class (or its descendants). Class helpers are a way to extend a class without using inheritance. A class helper simply introduces a wider scope for the compiler to use when resolving identifiers. When you declare a class helper, you state the helper name, and the name of the class you are going to extend with the helper. You can use the class helper any place where you can legally use the extended class. The compiler’s resolution scope then becomes the original class, plus the class helper. Class helpers provide a way to extend a class, but they 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.

Penulisan deklarasi statement class helper adalah sebagai berikut:

type
   identifierName = class helper [(ancestor list)] for classTypeIdentifierName
     memberList
   end;

Penggunaan

Misalkan kita mempunyai kelas TMyClass, sebagai berikut :

type
   TMyClass = class
      procedure MyProc;
      function  MyFunc: Integer;
   end;

Maka ketika kita ingin mengembangkan kelas TMyClass -misalkan ingin menambahkan method atau properti- tanpa melakukan perubahan apapun pada kode yang sudah ada, kita harus membuat kelas turunan, sebagai berikut:

type
   TMyClass = class
      procedure MyProc;
      function  MyFunc: Integer;
   end;

   TMyClassDescendant = class(TMyClass)
      procedure HelloWorld;
   end;

Nah, dengan class helper, kita tidak perlu membuat kelas turunan, cukup tambahkan method atau properti yang ingin ditambahkan pada class helper dan langsung siap digunakan.

type
   TMyClass = class
     procedure MyProc;
     function  MyFunc: Integer;
   end;

   TMyClassHelper = class helper for TMyClass
     procedure HelloWorld;
     function  MyFunc: Integer;
   end;

...

var
  X: TMyClass;
begin
  X := TMyClass.Create;
  X.MyProc;    // Calls TMyClass.MyProc
  X.HelloWorld; // Calls TMyClassHelper.HelloWorld
  X.MyFunc;    // Calls TMyClassHelper.MyFunc

Contoh Kasus

Lalu, bagaimanakah manfaat penggunaan class helper dalam ‘real-world’? Berikut contohnya.

Permasalahan

Misalkan kita ingin menyimpan nilai atau indeks masing-masing item yang terdapat di dalam ListBox. Misalnya item ‘Satu’ mempunyai indeks 70, item ‘Lima’ mempunyai indeks 300. Dalam hal ini indeks disimpan sebagai Object Integer yang terkait pada masing-masing item (pada kesempatan ini saya tidak akan membahas mengenai trik penyimpanan indeks sebagai object). Nah untuk mengakses object pada suatu item, kita menggunakan properti Objects:

  AListBox.Items.Objects[Index: Integer]: TObject;

Nah, dalam kasus ini, kita ingin mempercepat akses setiap object dengan langsung mengaitkannya pada properti Items dan dapat menggunakan index bertipe string, bukan Integer. Sehingga skenario akses nya adalah sebagai berikut:

  AListBox.Items[Index: string]: TObject;
  AListBox.Items.ObjectsByName[Index: string]: TObject;

Solusi

Untuk itu kita perlu membuat class helper untuk kelas TStrings, karena merupakan induk dari properti Items.

type
  TStringsHelper = class helper for TStrings
  protected
    function GetObject(Index: string): TObject; overload;
    procedure PutObject(Index: string; AObject: TObject); overload;
  public
    property ObjectsByName[Index: string]: TObject read GetObject write PutObject; default;
  end;

...

function TStringsHelper.GetObject(Index: string): TObject;
begin
  Result := Self.Objects[Self.IndexOf(Index)];
end;

procedure TStringsHelper.PutObject(Index: string; AObject: TObject);
begin
  Self.Objects[Self.IndexOf(Index)] := AObject;
end;

Pada kode diatas, kita membuat sebuah kelas TStringsHelper yang merupakan ‘helper’ dari kelas TStrings. Kelas TStringsHelper menambahkan dua protected method yaitu GetObject dan PutObject yang keduanya merupakan method overload karena kedua method tersebut sudah ada pada kelas TStrings namun dengan parameter dan implementasi yang berbeda. Implementasi method tersebut menggunakan method IndexOf untuk mendapatkan index integer yang kemudian nilai tersebut dimasukkan sebagai parameter index pada proeprti Objects.

Kemudian pada bagian public, ditambahkan deklarasi properti ObjectsByName dimana indexer yang digunakan bertipe string. Operasi read dan write properti ini diarahkan ke method GetObject dan PutObject yang baru, yaitu yang menerima parameter index berupa string. Dengan demikian skenario kedua sudah terpenuhi (AListBox.Items.ObjectsByName[Index: string]: TObject).

Lalu bagaimana dengan skenario pertama, yaitu akses langsung object melalui properti Items (AListBox.Items[Index: string]: TObject). Sangat mudah, cukup mendefinisikan properti tersebut dengan directive default.

Dan ketika properti Items di akses, maka method ObjectByName sudah tampak di CodeInsight dan tentu saja sudah siap digunakan.

CodeInsight for ObjectByName

CodeInsight for GetObject

Dan berikut ketika aplikasi dijalankan:

Skrisyut menggunakan default Items[]:
Akses Items[]

Skrisyut yang lain, akses menggunakan method ObjectByName:
Akses ObjectByName

Secara lengkap, kodenya adalah sebagai berikut:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TIntType = class
  private
    FValue: Integer;
    procedure SetValue(const Value: Integer);
  public
    constructor Create(Value: Integer);
    property Value: Integer read FValue write SetValue;
  end;

  TStringsHelper = class helper for TStrings
  protected
    function GetObject(Index: string): TObject; overload;
    procedure PutObject(Index: string; AObject: TObject); overload;
  public
    property ObjectsByName[Index: string]: TObject read GetObject write PutObject; default;
  end;

  TfrmClassHelperDemo = class(TForm)
    ListBox: TListBox;
    LabeledEdit: TLabeledEdit;
    btnRetrieveValue: TButton;
    lblInfo: TLabel;
    lblAbout: TLabel;
    procedure btnRetrieveValueClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
    procedure FreeObjects;
    procedure AssignObject(Index: Integer);
  public
    { Public declarations }
  end;

var
  frmClassHelperDemo: TfrmClassHelperDemo;

implementation

{$R *.dfm}

{ TIntType }

constructor TIntType.Create(Value: Integer);
begin
  FValue := Value;
end;

procedure TIntType.SetValue(const Value: Integer);
begin
  FValue := Value;
end;

{ TStringsHelper }

function TStringsHelper.GetObject(Index: string): TObject;
begin
  Result := Self.Objects[Self.IndexOf(Index)];
end;

procedure TStringsHelper.PutObject(Index: string; AObject: TObject);
begin
  Self.Objects[Self.IndexOf(Index)] := AObject;
end;

{ TfrmClassHelperDemo }

procedure TfrmClassHelperDemo.btnRetrieveValueClick(Sender: TObject);
var
  StrIndex: string;
begin
  StrIndex := LabeledEdit.Text;
  ShowMessage('Items[Index: string]' + #13#10 + 'Value of Index ' + QuotedStr(StrIndex) + ' is ' +
              IntToStr(TIntType(ListBox.Items[strIndex]).Value));
  ShowMessage('Items.ObjectByName[Index: string]' + #13#10 + 'Value of Index ' + QuotedStr(StrIndex) + ' is ' +
              IntToStr(TIntType(ListBox.Items.ObjectsByName[StrIndex]).Value));
end;

procedure TfrmClassHelperDemo.AssignObject(Index: Integer);
var
  IntObj : TIntType;
begin
  IntObj := TIntType.Create(Random(10000));
  ListBox.Items.Objects[Index] := TObject(IntObj);
end;

procedure TfrmClassHelperDemo.FreeObjects;
var
  I: Integer;
  IntObj : TIntType;
begin
  for I := 0 to ListBox.Items.Count - 1 do
  begin
    if Assigned(ListBox.Items.Objects[I]) then
    begin
      if (ListBox.Items.Objects[I] is TIntType) then
      begin
        IntObj := TIntType(ListBox.Items.Objects[I]);
        IntObj.Free;
        ListBox.Items.Objects[I] := nil;
      end;
    end;
  end;
end;

procedure TfrmClassHelperDemo.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeObjects;
end;

procedure TfrmClassHelperDemo.FormShow(Sender: TObject);
var
  I: Integer;
begin
  Randomize;
  for I := 0 to ListBox.Items.Count - 1 do
    AssignObject(I);
end;

end.

Semoga bermanfaat.

Categories: Delphi, Did You Know
  1. April 2, 2007 at 11:41 am

    Mantaaap! hihi kaya’ koment bang Luri aja 🙂

    Tapi rasa2nya kalo sample-nya kaya’ gitu ngga’ ada bedanya ama bikin class turunan biasa bro
    Ato jangan2 daku yg kurang awas merhatiin pembelajaran dikau ya xoxo…

  2. bprasetio
    April 2, 2007 at 12:05 pm

    ehmm tujuan dari class helper kan ‘mengembangkan kelas tanpa membuat kelas turunan dan tanpa memodifikasi kode kelas tersebut secara langsung‘.. jadi properti atau method tambahan dibuat dari luar kode dan dapat langsung diakses dari kelas tersebut, tidak ada utak atik modifikasi pada kelas yg dimaksud, termasuk kelas (dan komponen) yang sudah disediakan oleh Delphi, seperti TStrings, TLIstBox, dan sebagainya. coba baca quote dari Help Delphi yg saya sertakan dan lihat gambar maupun source code demo nya..

  3. April 4, 2007 at 9:39 am

    emang sangat-sangat berguna class helper ini…., klo di java – c# – PHP ada ga ya?

  4. bprasetio
    April 10, 2007 at 11:06 am

    mas kecret… class helper emang sangat berguna, eksplorasi lebih lanjut mengenai class helper dapat disimak pada tulisan saya berikutnya yaitu Overloading Default Array Properties. Check it out. 🙂

    btw mengenai class helper di java c# php, saya belum tahu, saya belum eksplor lebih jauh.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: