Jika Anda menjadi programmer di platform
Microsoft, tentunya Anda akan bertemu dengan makhluk bernama COM (Component
Object Model). COM digunakan dari membuat addins di Office sampai membuat game
dengan DirectX.
Tentunya Anda pernah melihat kode sampel
dengan tulisan “CoCreateInstance”
bukan?
Terkadang ada library yg belum ada
padanannya di .Net, tapi ada versi COM-nya. Nah, oleh karena itu marilah kita
belajar COM daripada menghindarinya.
Untuk Part One ini, saya akan membahas
tulang punggung COM, yaitu konsep interface.
Pertama, buka dokumentasi MSDN, dan cari
info tentang methods yg ada di class CWnd
(bagian dari C++ MFC Library). Anda akan melihat seperti berikut:

Class CWnd adalah ibu dari controls,
frames, views dan elemen window lainnya. Dalam dokumentasi MSDN terlihat bahwa
methods CWnd dipartisi berdasarkan fungsi; contoh ada Drag-n-Drop Functions dan
Data-Binding Functions.
Sekarang coba buat sebuah aplikasi MFC
baru di Visual Studio. Kemudian buatlah sebuah objek bertipe CWnd dan lihat berapa banyak method
yang ada di instance ini setelah Anda ketik . (dot operator)!

Class CWnd hanya memiliki satu public
interface, dan interface ini memikul terlalu banyak tanggung jawab. Untuk bikin
menu, pake interface ini. Untuk akses clipboard pake interface ini.
Padahal seperti ditunjukkan oleh MSDN
Library, satu public interface yang berisi ratusan methods ini dapat
dikelompokkan berdasarkan fungsi.
So, bayangkan jika CWnd meng-ekspos
beberapa interface seperti IMenu dan IClipboard. Kita akan menulis kode seperti
berikut:
bool result = false;
IMenu
*pMenu = NULL;
IClipboard
*pClip = NULL;
// Activate the CWnd object
CreateCWndObj();
// Support IMenu kagak?
result =
GetInterfaceFromCWnd(IMENU, (void**)&pMenu);
if (result) {
pMenu->Draw("Selamat
datang di dunia interface-based programming");
}
// Support IClipboard?
result =
GetInterfaceFromCWnd(ICLIPBOARD, (void**)&pClip);
if (result) {
pClip->Copy();
}
Kodenya memang lebih panjang, tapi jika
kita ketik pClip-> maka
Intellisense Visual Studio hanya akan menampilkan method yg berhubungan dengan
Clipboard, seperti Copy dan Paste. Kita tidak akan dapat method
seperti ShowCaret() dan lainnya yang
tidak berhubungan dengan interface IClipboard.
Sebuah class yang hanya memiliki satu
interface tapi ratusan methods seperti CWnd dapat mengintimidasi pengguna class
tersebut. Ketika user menggunakan IClipboard, user hanya bekerja dengan satu behavior dari class tersebut.
Sebelum melangkah ke COM interfaces,
sementara ini kita definisikan interface sebagai berikut (dlm bhs Inggris agar
tidak hilang maknany):
An
interface
is a collection of a semantically related
functions, which describe a single and unique behavior that may be supported by
a class.
Diagram Interface
Notasi interface digambar seperti “lollipop”.
Untuk contoh custom CWnd kita diatas, berikut gambarnya:
Gambar seperti diatas akan banyak Anda
jumpai di literatur tentang COM. COM disebut sebagai interface-based programming paradigm. Semua hal di COM berpijak
pada creation dan implementation tentang interfaces.
Interface terasa sama seperti public
interface sebuah standar C++/C# class, bedanya ada 2:
-
interface
tidak berisi kode implementasi
-
interface
tidak pernah mendefinisikan state
Enkapsulasi yg lebih Dalem
Hal paling penting dari interface-based
programming adalah pengguna object tidak boleh mengakses fungsi-fungsi
yang tersedia dari object instance, melainkan dari interface yang di-support.
Contohnya adalah kode berikut:
interface IDraw
{
virtual void Draw() = 0;
}
interface ITransform
{
virtual void Rotate(float
degrees) = 0;
virtual void Scale(float
size) = 0;
}
class Lingkaran
: public IDraw,
public
ITransform
{
public:
// IDraw
void Draw();
// ITransform
void Rotate(float degrees);
void Scale(float size);
private:
// state untuk
implementasi interface
}
Ketika di-compile, C++ compiler akan
menghasilkan layout virtual table berikut:
Terus dalemnya enkapsulasi dimana? Jadi
ketika kita mendapatkan handle ke interface IDraw (IDraw_VirtualPointer), itu
sebenarnya mengarah ke virtual table IDraw (yg hanya berisi Draw). Kita tidak
bisa mengakses fungsi Rotate() walaupun Rotate() terdefinisikan di class yg
sama!
Interface Versioning
Setelah interface di-publish, tidak baik
untuk mengubah ubah interface tersebut. Lantas bagaimana meng-extend sebuah interface? Dengan sebuah
teknik bernama interface inheritance.
Contohnya jika kita ingin meng-extend
IDraw dari kode di atas, adalah seperti berikut:
interface IDraw2 : public IDraw
{
virtual void DrawKeMemory() = 0;
}
// Setelah 6 bulan, kita ingin
// draw ke file
interface IDraw3 : public IDraw2
{
virtual void DrawKeFile() = 0;
}
Ini akan menghasilkan layout seperti
berikut:
Jadi kalo kita ingin menggunakan class yg
implement semua fungsi Draw() yang terbaru, kodenya menjadi seperti ini:
class Lingkaran
: public IDraw3
{
// IDraw3 = IDraw
+ IDraw2 + IDraw3
void Draw();
void
DrawKeMemory();
void
DrawKeFile();
}
// Kode ini salah
class Lingkar : public IDraw,
public
IDraw2,
public
IDraw3
{
// . . .
}
Konklusi dan Sample Code
Interface-based programming bisa
dikatakan sebagai refinement dari OOP
klasik, dan fondasi dari awal belajar COM. Logically, interface adalah koleksi
beberapa fungsi yg mendeskripsikan satu behavior. Physically, interface adalah
sebuah set terdiri dari bebera pure virtual functions.
Berikut sample code untuk menjelaskan
prinsip interface-based programming yang telah dibahas:
// main.cpp
#include <iostream>
using namespace std;
// ada definisi interface disini
#include <windows.h>
interface IDraw
{
virtual void Draw() = 0;
};
interface ITransform
{
virtual void Rotate(float
degrees) = 0;
virtual void Scale(float
size) = 0;
};
class Kotak : public IDraw,
public
ITransform
{
public:
Kotak() {}
virtual
~Kotak() {}
// IDraw
virtual void Draw() {
cout << "Kotak::Draw()
dipanggil"
<< endl;
}
// ITransform
virtual void Rotate(float
degrees) {
cout << "Rotasi
dengan derajat "
<< degrees
<< endl;
}
virtual void Scale(float
size) {
cout << "Resize
object sebesar "
<< size << " unit"
<< endl;
}
};
// Kotak API
// ini semua tersedia oleh COM
// jika kita membuat COM library
Kotak
*pKotak;
void
CreateKotak() {
pKotak = new
Kotak();
}
void
DestroyKotak() {
delete
pKotak;
}
enum
INTERFACEID {
IDRAW = 0,
ITRANSFORM = 1,
IMENU = 2,
};
bool
GetInterfaceFromKotak(INTERFACEID id, void**
ifacePtr) {
if (pKotak
== NULL) {
cout << "Lupa
bikin Kotak nih yee..." << endl;
return false;
}
if (id ==
IDRAW) {
*ifacePtr = (IDraw*) pKotak;
return true;
}
if (id ==
ITRANSFORM) {
*ifacePtr = (ITransform*) pKotak;
return true;
}
*ifacePtr = NULL;
cout << "Kotak
tidak support ID: " << id << endl;
return false;
}
int main()
{
bool result
= false;
IDraw *pDraw = NULL;
ITransform *pTrans = NULL;
// Activate Kotak
CreateKotak();
// Bisa dapet
IDraw interface nggak?
result = GetInterfaceFromKotak(IDRAW, (void**) &pDraw);
if (result)
{
pDraw->Draw();
}
// ITransform
bisa?
result = GetInterfaceFromKotak(ITRANSFORM, (void**) &pTrans);
if (result)
{
pTrans->Rotate(90);
pTrans->Scale(2);
}
result = GetInterfaceFromKotak(IMENU, (void**) &pTrans);
// Destroy
DestroyKotak();
return 0;
}
Referensi:
Developer's Workshop to COM and ATL 3.0 , Andrew W. Troelsen, Wordware.