You are on page 1of 12

Dasar Penggunaan Interface di Pascal

Interface berarti antar muka. Ia adalah jembatan antara sistem dan user yang menggunakan sistem
itu. Interface dibuat untuk mengabstraksi sistem. Pada kendaraan, kemudi, pedal gas, pedal rem
adalah interface. Pengemudi cukup tahu kegunaan masing-masing. Kemudi untuk berbelok, pedal
gas dan rem untuk menambah dan mengurangi kecepatan. Detail implementasi bagaimana agar
kendaraan itu bisa berbelok tidak penting bagi pengemudi. Kendaraan itu bisa berbelok karena roda
depannya (seperti pada mobil umumnya) atau karena roda belakang (misal fork lift) tidak penting,
yang penting bagi pengemudi, putar kemudi ke kiri berarti kendaraan belok kiri atau sebaliknya.

Interface adalah kontrak kesepakatan dan aturan antara pembuat sistem dan pengguna sistem.
USB adalah interface. Ia mengatur kesepakatan antara pembuat piranti dengan penggunanya.
Sepanjang piranti menggunakan mekanisme USB, komputer yang memanfaatkan piranti tersebut
dapat terhubung dengannya tidak peduli bahwa piranti tersebut adalah keyboard atau pengeras
suara.

Dalam pemrograman, istilah interface mirip dengan contoh di atas. Ia adalah kontrak kesepakatan
antara sistem dan pengguna sistem.

Interface di Pascal
Di pemrograman Pascal, kata tercadang interface punya dua kegunaan:

1. Penanda bagian deklarasi pada sebuah unit yang dapat diakses dari program atau unit lain
(Listing 1).
2. Deklarasi tipe interface (Listing 2).

Listing 1.

unit namaunit;
interface
//deklarasi sesuatu
implementation
//bagian implementasi kode
initialization
//bagian inisialisasi
end.

Listing 2.
unit namaunit;
interface
type
IDrawable = interface
procedure draw();
end;
IPaintable = interface (IDrawable)
procedure paint();
end;
implementation
end.

Di sini kita hanya fokus pada penggunaan interface untuk deklarasi tipe.

Interface vs kelas abstrak


Kelas abstrak adalah kelas yang memiliki satu atau lebih metode yang dideklarasi dengan kata
tercadang abstract (Listing 3).

Interface seperti halnya kelas dapat diturunkan dari interface lain. Pada Listing 2,
interface IPaintable akan memiliki metode paint() dan draw(). Anda bebas memberi nama
interface tetapi di Pascal, konvensi penamaannya biasanya diawali dengan huruf kapital I untuk
interface.

Kelas abstrak dan interface memiliki persamaan yaitu bahwa mereka memiliki satu atau lebih
deklarasi metode yang implementasinya tidak tersedia. Untuk kelas abstrak, metode tersebut harus
diimplementasi oleh kelas turunannya. Untuk interface, metode tersebut harus diimplementasi oleh
kelas yang dideklarasi memanfaatkan interface tersebut.

Listing 3.

unit biological;

{$mode objfpc}{$H+}

interface

uses
core;

type

TBiologicalObject = class(TCoreObject)
public
procedure breathe(); virtual; abstract;
end;

implementation

end.

Cara menggunakan kelas abstrak vs interface


Jika kita punya deklarasi kelas seperti Listing 4.

Listing 4.

type
TMyAbstractClass = class(TObject)
procedure saySomething(); virtual; abstract;
end;
TMyDerivedClass = class(TMyAbstractClass)
procedure saySomething(); override;
end;

Kode di Listing 5 berikut adalah cara kita menggunakan kelas abstrak tersebut.

Listing 5.

var myClass : TMyAbstractClass;

myclass := TMyDerivedClass.Create();
myclass.saySomething();
myclass.Free();

Untuk interface.

Listing 6.

type
IMyInterface = interface
procedure saySomething();
end;
TMyInterfaceClass = class(TInterfacedObject, IMyInterface)
procedure saySomething();
end;

Kode di Listing 7 berikut adalah cara kita menggunakan interface tersebut.

Listing 7.

var myInterface : IMyInterface;

myInterface := TMyInterfaceClass.Create();
myInterface.saySomething();

Listing 5 dan Listing 6 tidak jauh berbeda, lalu mengapa harus menggunakan interface?

Issue dengan single inheritance


Jika kita punya struktur objek seperti Gambar 1 berikut:

Gambar 1. Diagram struktur objek awal.


Kita ingin mengubah kelas THuman dan TPainterRobot di atas dengan menambah kemampuan untuk
menggambar (metode draw()) dan menambah sebuah kelas baru yang bertanggung jawab atas
proses penggambaran bernama TCanvasObject sehingga menjadi seperti struktur objek pada
Gambar 2.

Gambar 2. Diagram struktur objek yang baru.


TCanvasObject tergantung pada kelas THuman dan TPainterRobot untuk menyediakan fungsionalitas
menggambar.
Solusi 1
Kita tentu dapat menyelesaikan dengan kode seperti Listing 8.

Listing 8.

type
TCanvasObject = class(TObject)
private
drawableObject : TCoreObject;
public
constructor Create(drawableObj : TCoreObject);
procedure drawCanvas();
end;

dengan implementasinya seperti berikut:

Listing 9.

constructor TCanvasObject.Create(drawableObj : TCoreObject);


begin
drawableObject := drawableObj;
end;

procedure TCanvasObject.drawCanvas();
begin
if (drawObject is THuman) then
begin
THuman(drawableObject).draw();
end else
if (drawObject is TPainterRobot) then
begin
TPainterRobot(drawableObject).draw();
end;
end;
Untuk menggunakan TCanvasObject, bisa dengan cara seperti Listing 10.

Listing 10.

var canvasObject : TCanvasObject;


drawableObject : TCoreObject;

drawableObject := THuman.Create();
canvasObject := TCanvasObject.Create(drawableObject);
canvasObject.drawCanvas();

Kekurangan kode Listing 9, kelas TCanvasObject harus tahu deklarasi


kelas THuman dan TPainterRobot. Bila kemudian hari, kita ingin menambahkan kelas lain yang
mampu menggambar, daftar if .. then .. else pada Listing 9 semakin panjang.
Kode TCanvasObject semakin terikat ketat dengan kelas lain.

Karena deklarasi THuman dan TPainterRobot dapat diakses dalam TCanvasObject, metode lain dalam
kedua kelas tersebut (breathe(), doAutomateTask(), doMechanicalTask()) dapat diakses
dalam TCanvasObject meskipun sebenarnya metode tersebut tidak relevan. Kita hanya dapat
bergantung pada kedisiplinan programmernya untuk tidak memanggil kode kode yang tidak relevan.

Solusi 2
Cara kedua adalah dengan mendeklarasikan metode abstrak bernama draw() di TCoreObject.
Dengan cara ini Listing 9 dapat disederhanakan menjadi seperti Listing 11.
Kelas TCanvasObject tidak perlu tahu deklarasi THuman dan TPainterRobot.

Listing 11.

procedure TCanvasObject.drawCanvas();
begin
drawableObject.draw();
end;

Tapi ini berakibat semua kelas yang diturunkan dari TCoreObject dianggap memiliki kemampuan
menggambar, padahal kemampuan ini hanya relevan bagi kelas THuman dan TPainterRobot.

Solusi 3
Solusi ideal adalah dengan mengenalkan interface baru misal bernama IDrawable yang
deklarasinya seperti Listing 12.

Listing 12.
unit drawable;

{$mode objfpc}{$H+}

interface

uses
Classes, SysUtils;

type
IDrawable = interface
procedure draw();
end;

implementation

end.

Kelas TCoreObject diubah dengan menurunkannya dari TInterfacedObject seperti pada Listing 13.
Deklarasi kelas THuman dan TPainterRobot diubah menjadi Listing 14.

Listing 13.

TCoreObject = class(TInterfacedObject)
end;

Listing 14

THuman = class(TBiologicalObject, IDrawable)


public
procedure draw();
procedure breathe(); override;
end;
TPainterRobot = class(TRobot, IDrawable)
public
procedure draw();
procedure doMechanicalTask();override;
procedure doAutomateTask();override;
end;

Sedangkan deklarasi kelas TCanvasObject menjadi Listing 15.

Listing 15.

TCanvasObject = class(TObject)
private
drawableObject : IDrawable;
public
constructor Create(drawableObj : IDrawable);
procedure drawCanvas();
destructor Destroy(); override;
end;

constructor TCanvasObject.Create(drawableObj : IDrawable);


begin
drawableObject := drawableObj;
end;

destructor TCanvasObject.Destroy();
begin
drawableObject := nil;
inherited Destroy();
end;

procedure TCanvasObject.drawCanvas();
begin
drawableObject.draw();
end;

Sedangkan deklarasi drawableObject Listing10 sedikit diubah menjadi seperti Listing 16.

Listing 16

var canvasObject : TCanvasObject;


drawableObject : IDrawable;

drawableObject := THuman.Create();
canvasObject := TCanvasObject.Create(drawableObject);
canvasObject.drawCanvas();

Loose Coupling
Interface membantu menulis kode yang ikatan dengan kode lain seminimal mungkin. Dengan
keterikatan yang minimal, kita dapat mengubah kode lebih mudah.

Pada contoh Listing 15, karena kelas TCanvasObject hanya tergantung pada interface IDrawable, ia
hanya hanya dapat mengakses metode draw(). Metode lain milik
kelas THuman atau TPainterRobot tidak dapat diakses dari dalam TCanvasObject. Kita dapat dengan
mudah mengganti implementasi lain sepanjang instance objek mengimplementasi
interface IDrawable. Contoh bila kita punya deklarasi kelas seperti Listing 17.

Listing 17.

TStrangeObject = class(TInterfacedObject, IDrawable)


public
procedure draw();
end;

Kita dapat mengganti baris

drawableObject := THuman.Create();

menjadi
drawableObject := TStrangeObject.Create();

pada Listing 16 dan kode kita tetap bekerja normal. Perhatikan bahwa TStrangeObject sama sekali
tidak ada kaitannya dengan kelas TCoreObject.

Satu kelas, banyak interface


Sebuah kelas hanya dapat diturunkan dari satu kelas orang tua. Tetapi satu kelas dapat
mengimplementasi satu atau lebih interface.

Jika misal ada kebutuhan kelas baru misal bernama TRepairCenter dan kelas baru ini membutuhkan
kelas THuman dan kelas TIndustrialRobot harus memiliki kemampuan untuk memperbaiki sesuatu.
Solusinya, kita buat deklarasi interface baru misal bernama ICanRepair.

Listing 18

ICanRepair = interface
['{EF4D5E9D-252D-4DEC-8351-0616B8E7B2C0}']
procedure repair();
end;

Deklarasi THuman pada Listing 14, kita ubah dengan menambah interface ICanRepair seperti berikut:

Listing 19

THuman = class(TBiologicalObject, IDrawable, ICanRepair)


public
procedure draw();
proecdure repair();
procedure breathe(); override;
end;

TIndustrialRobot = class(TRobot, ICanRepair)


public
procedure repair();
procedure doMechanicalTask();override;
procedure doAutomateTask();override;
end;

kelas TRepairCenter hanya mengakses ICanRepair mirip cara kerja TCanvasObject pada contoh
Listing 15.

Reference counting di interface


Di Delphi, deklarasi interface yang tidak ditentukan parent interface selalu diturunkan
dari IInterface yang tidak lain adalah nama alias untuk interface IUnknown. Interface ini memiliki
tiga metode _AddRef(), _Release() dan QueryInterface(). Ini karena awal dikenalkannya interface
adalah untuk mendukung Component Object Model (COM) milik Windows.

Jika interface IDrawable diturunkan dari IUnknown, kenapa kita tidak pernah membuat
implementasi QueryInterface(), _AddRef() ataupun _Release()? Ini karena semuanya sudah
diimplementasi oleh kelas TInterfacedObject (Listing 13).
Metode _AddRef() dan _Release() ditandai dengan awalan _ untuk menandakan ini adalah metode
yang seharusnya tidak perlu dipanggil oleh programmer.

Ketika kita melakukan assignment ke variabel bertipe interface seperti pada contoh kode Listing 16,
kompiler akan menyisipkan kode pemanggilan _AddRef() dan ketika variabel tersebut sudah tidak
digunakan (out of scope) maka kompiler akan menyisipkan pemanggilan _Release(). Melakukan
assignment nilai nil ke variabel bertipe interface juga akan memicu penyisipan kode _Release().

Implementasi _AddRef() pada TInterfacedObject adalah menambah jumlah referensi dan


implementasi _Release() menguranginya nilai referensi ini. Bila jumlah referensi bernilai 0, maka
metode Free() akan dipanggil. Ini alasan mengapa pada Listing 7 sama sekali tidak ada
pemanggilan Free() disana.

Yang harus kita perhatikan adalah, kita harus konsisten menggunakan interface dan tidak
mencampur aduk pemanggilan kelas agar proses reference counting ini berjalan benar
yakni _AddRef() dan _Release() saling berpasangan. Jika tidak, ia akan menyebabkan memory leak
atau access violation karena instance objek di hapus dari memori sebelum waktunya.

Jika kita tidak menginginkan automatic reference counting ini, kita bisa membuat
implementasi IUnknown sendiri. Jika kita tempuh cara ini, manajemen memori interface sepenuhnya
ditangan programmer.

Di Free Pascal, deklarasi interface yang tidak menentukan parent interface secara defaultnya sama
seperti Delphi. Bedanya bisa diubah. Khusus untuk Free Pascal, dikenalkan
direktif $INTERFACES yang nilainya bisa diset COM untuk perilaku yang sama seperti Delphi
atau CORBA untuk tidak menggunakan IUnknown sebagai root parent. Bila tidak ditentukan, defaultnya
adalah COM.

Bila menggunakan direktif {$INTERFACES CORBA}, ini berarti tidak ada lagi penyisipan
kode _AddRef() dan _Release() dan manajemen memori sepenuhnya tanggung jawab programmer.
Jika Anda menggunakan CORBA, interface lebih ramping lagi karena tidak ada
metode _AddRef(), _Release() dan QueryInterface(), mirip dengan interface pada bahasa lain
seperti Java.

GUID

You might also like