Professional Documents
Culture Documents
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 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.
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.
myclass := TMyDerivedClass.Create();
myclass.saySomething();
myclass.Free();
Untuk interface.
Listing 6.
type
IMyInterface = interface
procedure saySomething();
end;
TMyInterfaceClass = class(TInterfacedObject, IMyInterface)
procedure saySomething();
end;
Listing 7.
myInterface := TMyInterfaceClass.Create();
myInterface.saySomething();
Listing 5 dan Listing 6 tidak jauh berbeda, lalu mengapa harus menggunakan interface?
Listing 8.
type
TCanvasObject = class(TObject)
private
drawableObject : TCoreObject;
public
constructor Create(drawableObj : TCoreObject);
procedure drawCanvas();
end;
Listing 9.
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.
drawableObject := THuman.Create();
canvasObject := TCanvasObject.Create(drawableObject);
canvasObject.drawCanvas();
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
Listing 15.
TCanvasObject = class(TObject)
private
drawableObject : IDrawable;
public
constructor Create(drawableObj : IDrawable);
procedure drawCanvas();
destructor Destroy(); override;
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
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.
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.
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
kelas TRepairCenter hanya mengakses ICanRepair mirip cara kerja TCanvasObject pada contoh
Listing 15.
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().
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