Contoh output implementasi Anonymous procedure di Delphi

Print Friendly

Beberapa waktu lalu, saya sudah menulis sekilas tentang Extended Record atau Advanced Record, yang merupakan fitur bahasa Delphi.    Tapi bukan itu saja fitur bahasa Delphi. Masih ada fitur lain yang masih jarang kita gunakan: Anonymous Procedure atau disebut juga Closures.

Apakah Anonymous Procedure Di Delphi Itu?

Mari kita lihat.

Bila Anda terbiasa menggunakan jQuery untuk memanipulasi array atau melakukan looping pada DOM Object (misalnya looping pada semua tag <A> dalam sebuah tag <div> dengan id “container”), saya percaya bahwa Anda juga sering menggunakan jQuery.each(); yang adalah helper untuk array.

$('#container a').each(function(){
  alert($(this).attr('href') + ': '+ $(this).text());
});

Di mana helper .each() menggunakan sebuah parameter function() yang merupakan callback function. Callback function ini dijalankan untuk setiap elemen dalam array yang ditemukan oleh helper .each();. Hebatnya (di JavaScript), callback function ini tidak perlu dideklarasikan, tapi dapat langsung digunakan dengan mengimplementasikan kodenya langsung sebagai parameter pada .each();

Kode callback yang langsung diimplementasikan di atas itulah yang akan kita bahas di sini, dengan memanfatkan fitur Delphi (sejak Delphi 2009): Anonymous Procedure.

Sebagai contoh, saya akan menggunakan class TstringList yang merupakan class standar bawaan Delphi. Tidak terbilang banyaknya penggunaan class TstringList ini dalam kode kita sehari-hari, meskipun kita tidak sadari. TComboBox.Items, TMemo.Lines, TListBox.Items, semuanya adalah implementasi TStringList (Meskipun kita melihat deklarasinya sebagai TStrings yang merupakan Ancestor virtual untuk TStringList).

Saya akan menambahkan sebuah helper method (metode bantu) untuk TStringList ini, yaitu metode each(). Metode ini akan melakukan looping pada semua elemen string dalam TStringList dan memanggil sebuah callback function pada setiap elemen string yang ditemukan (mirip dengan implementasi pada jQuery di atas).

Kita awali dengan membuat sebuah projek baru (Delphi 2009 ke atas, untuk pengguna Delphi 7 dkk., silahkan jadi pembaca saja :) ).

Tambahkan sebuah TMemo dan sebuah TButton ke form. Kita anggap namanya masing-masing adalah Memo1 dan Button1. Lihat gambar berikut sebagai perbandingan:

Setelahnya, lihat source code Form dengan menekan tombol F12. Anggap nama unitnya adalah unit1. Di atas deklarasi class Tform1, kita tambahkan sebuah deklarasi class untuk mengoverride TStringList yang asli:

type
  TStringList = class(Classes.TStringList)
    procedure each(callback: TProc<integer,string>);
  end;
  //-----------------------------------------
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

Dengan overriding ini, kita dapat menambahkan metode atau property baru ke class TStringList tanpa memodifikasi TstringList yang asli. Perubahan ini hanya berlaku dalam lingkup aplikasi yang menggunakan unit1 ini saja, tentunya dengan menambahkan nama unit1 ke klausa uses di unit pemanggil.

Untuk lebih jelas mengenai class overriding dan contoh implementasinya, silahkan lihat di AdvancedEdit, Cara Mudah Memeriksa Input TEdit Di Delphi.

Sudah? Lihat, kita menambahkan sebuah metode baru ke class TStringList, yaitu metode each(). Parameter pada metode each() ini sepintas agak aneh: procedure each(callback: TProc<integer,string>);. Tapi inilah fitur yang saya maksud. TProc sendiri adalah type “reference to procedure”, bisa dikatakan referensi ke sebuah tipe prosedur. Sementara tag <integer,string> adalah tipe data yang akan digunakan sebagai parameter dalam callback.
Dengan deklarasi prosedur procedure each(callback TProc<integer,string>),Delphi akan menganggap bahwa callback adalah sebuah prosedur dengan type: procedure(integer; string);. Dalam implementasi nanti, prosedur ini tidak perlu diberi nama sebagaimana prosedur biasa, sehingga disebut anonymous procedure.

Bila Anda merasa agak sulit memahami, lanjut saja. Nanti di contoh real di bawah, akan lebih mudah kita pahami.
Tekan Ctrl+C untuk membuat implementasi metode each() ini:

procedure TStringList.each(callback: TProc<integer,string>);
var
  i: integer;
begin
  if self.Count = 0 then
    exit;
  for i:= 0 to Self.Count - 1 do
  begin
    callback(i, Self[i]);
  end;
end;

Kode ini akan melakukan looping pada setiap elemen string yang terdapat pada TStringList. Bila ternyata TStringList belum memiliki isi string apapun (self.count = 0), prosedur akan berhenti dan melakukan exit. Bila ada elemen yang ditemukan, prosedur each() ini akan memanggil prosedur callback dengan menyertakan dua parameter, yaitu indeks elemen dan isi elemen: callback(i, Self[i]);. Lihat, i adalah index pada looping, sedangkan Self[i] adalah elemen string pada index ke-i.
Bila Anda perhatikan lagi deklarasi metode procedure TStringList.each(callback: TProc<integer,string>);, Anda akan mendapat gambaran kenapa ada tag TProcTProc<integer,string> di situ, yang diterjemahkan oleh Delphi sebagai procedure(integer; string); dan hubungannya dengan pemanggilan callback(i, Self[i]);.

Kita kembali ke contoh.
Klik ganda Button1 dan isi kode seperti berikut:

procedure TForm1.Button1Click(Sender: TObject);
begin
  TstringList(Memo1.Lines).each(
    procedure (i:integer; s: String)
    begin
      ShowMessage('Indeks ke-'+IntToStr(i)+' berisi : '+s);
    end
  );
end;

Di sini serunya.
Perlu dicatat bahwa Memo1.Lines bertipe TStrings, sehingga kita cast dulu menjadi TStringList dengan TStringList(Memo1.Lines). Kemudian, kita eksekusi metode each() dengan sebuah parameter bertipe procedure, langsung diimplementasikan di tempat.

Bandingkan dengan cara yang biasa kita pakai:

procedure TForm1.Button1Click(Sender: TObject);
var
 i: integer;
begin
  if Memo1.Lines.Count = 0 then
    exit;
  for i:=Memo1.Lines.count -1 do
  begin
    ShowMessage('Indeks ke-'+IntToStr(i)+' berisi : '+Memo1.Lines[i]);
  end;
end;

Keduanya menghasilkan output yang sama. Bedanya, dengan cara pertama menggunakan anonymous procedure kita tidak perlu lagi melakukan looping. Kita cukup fokus pada apa yang akan kita lakukan terhadap elemen-elemen string saja.

Anda dapat menjalankan projek, mengisi Memo1 dengan beberapa baris string, dan klik Button1 untuk melihat outputnya.

Selain sebagai prosedur, kita juga dapat memanfaatkan Anonymous Procedure ini dalam bentuk fungsi yang memiliki nilai kembalian.
Sampai sini dulu. Insya Allah, jika ada kesempatan, kita lihat lebih jauh manfaat dan contoh implementasi fitur Anonymous Procedure ini dalam aplikasi kita.

Sukses selalu :)

Comments

comments

Powered by Facebook Comments