telepon4cpp Pemrograman berorientasi objek (OOP) sangat boleh jadi merupakan teknik pemrograman terbaik saat ini. Kemampuannya mengorganisir program dalam bentuk objek-objek, sangat cocok diterapkan dalam pembuatan program yang komplek dan berskala besar. Penggunaan objek-objek yang reusable dan extensible sebagai bahan dasar pembuatan program menjadikan program tidak hanya mudah dipelihara tapi juga mudah dikembangkan.

Salah satu komponen pemrograman berorientasi objek adalah abstraksi data. Komponen ini merupakan komponen dasar yang harus kita pahami untuk dapat menguasai konsep dasar pemrograman berorientasi objek. Dalam artikel ini saya akan membahas tentang komponen dasar tersebut. Apa itu abstraksi data, mengapa abstraksi data perlu dilakukan, dan bagaimana penerapannya dalam program adalah pertanyaan-pertanyaan yang menjadi pokok bahasan dalam artikel ini.

Dalam tulisan ini Anda dapat menyimak evolusi abstraksi data dari teknik pemrograman terstruktur ke teknik pemrograman berorientasi objek. Melalui empat buah contoh program, yaitu TELEPON1.CPP, TELEPON2.CPP, TELEPON3.CPP, dan TELEPON4.CPP, saya akan mengajak Anda melihat dan merasakan pentingnya (keuntungan) abstraksi data dalam sebuah program, bagaimana abstraksi data dilakukan, dan sejauh mana abstraksi data dapat dilakukan dengan teknik pemrograman terstruktur dan teknik pemrograman berorientasi objek.

Semua listing program dalam tulisan ini saya uji menggunakan interpreter C/C++ Ch buatan SoftIntegration. Alasan saya menggunakan C++, bukan bahasa pemrograman lain seperti Pascal misalnya, karena scope pengaksesan data yang bersifat private (data yang disembunyikan dalam objek) pada C++ lebih ketat. Sebagai contoh, pada C++ data private dalam objek global hanya dapat diakses melalui fungsi-fungsi anggota objek tersebut yang bersifat public. Pada Pascal tidaklah demikian. Data private dalam sebuah objek global dapat diakses secara langsung dari manapun, tanpa melalui fungsi-fungsi anggota objek.

Jadi, untuk lebih mendukung konsep abstraksi data yang hendak saya sampaikan, maka saya menggunakan bahasa C++. Kalau Anda ingin mendapatkan gambaran yang jelas tentang perbedaan scope yang saya maksudkan, Anda dapat mencoba program SCOPE.PAS dan SCOPE.CPP berikut ini. Kedua program ini tidak perlu saya jelaskan karena telah dilengkapi dengan keterangan program yang jelas.

SCOPE.PAS

{
   SCOPE.PAS
   Scope pengaksesan data pada Pascal.
   Chandra MDE, Februari 1999
}

type
   objek = object  { definisi objek }
      private
         privateData: integer;  { data private }

      public
         procedure setData(i: integer);
         function getData : integer;
         procedure printData;
   end;

{ Implementasi fungsi-fungsi anggota objek }
   procedure objek.setData(i: integer);
   begin
      privateData:= i;
   end;

   function objek.getData : integer;
   begin
      getData:= privateData;
   end;

   procedure objek.printData;
   begin
      writeln(‘Data = ‘, privateData);
   end;

var
   OBJ: objek;   { deklarasi variabel objek global }

begin
{ Mengakses privateData melalui objek }
   OBJ.setData(10);
   OBJ.printData;
   OBJ.setData(100);
   writeln(‘Data = ‘, OBJ.getData);

{ Mengakses privateData secara langsung pun bisa }
   OBJ.privateData:= 200;
   writeln(‘Data = ‘, OBJ.privateData);
end.

Screenshot program menggunakan Turbo Delphi Explorer.

image

 

SCOPE.CPP

/*
   SCOPE.CPP
   Scope pengaksesan data private pada C++.
   Chandra MDE, Februari 1999
*/

#include <iostream.h>

class objek   // definisi objek
{
   private:
      int privateData;      // data private

   public:
      void setData(int i);  // fungsi-fungsi pemanipulasi
      int getData();        // yang bersifat public
      void printData();
};   // akhir definisi objek

// implementasi fungsi-fungsi anggota objek
void objek::setData(int i)
{
   privateData = i;
}

int objek::getData()
{
    return privateData;
}

void objek::printData()
{
    cout << "Data = " << privateData;
    cout << endl;
}

objek OBJ;  // deklarasi variabel objek

void main()
{
// Mengakses data private harus melalui fungsi-fungsi
// pemanipulasi yang bersifat public

   OBJ.setData(10);   // set privateData=10
   OBJ.printData();
   OBJ.setData(100);
   cout << "Data = " << OBJ.getData();
   cout << endl;

// Cobalah untuk mengakses data secara langsung
// seperti baris berikut ini:

//   OBJ.privateData = 100;

// atau seperti baris berikut ini:

//   cout << "Data = " << OBJ.privateData;

// Dijamin akan ERROR dengan pesan kesalahan sbb:
// Error: ‘objek::privateData’ is not accessible
}

Screenshot program menggunakan interpreter Ch.

image

Dari screenshot di atas, terbukti bahwa C++ lebih ketat dalam urusan data yang bersifat private pada sebuah objek atau class.

Nah, saya berharap dengan mengikuti jalannya evolusi abstraksi data dari program terstruktur ke program berorientasi objek, Anda akan mendapatkan gambaran yang jelas tentang pentingnya abstraksi data dan bagaimana abstraksi data dilakukan. Dan saya berharap Anda akan mengetahui bagaimana bahasa pemrograman berorientasi objek seperti C++ memudahkan kita dalam melakukannya. Syukur-syukur kalau setelah membaca artikel ini Anda tergugah untuk beralih dari teknik pemrograman terstruktur ke teknik pemrograman berorientasi objek.

Btw, selain menggunakan interpreter C/C++ Ch, saya juga menguji program dalam tulisan ini menggunakan kompiler freeware Borland C++ 5.5.1 dan menggunakan IDE C-Free 4.0. Berikut adalah screenshot IDE C-Free 4.0 yang menggunakan Borland C++ sebagai kompiler.

image

Abstraksi Data
Di sini saya akan mencoba menjelaskan tentang abstraksi data lewat gambaran-gambaran dan istilah-istilah yang berkaitan dengan abstraksi data yang semoga dapat menuntun penalaran kita dalam memahami apa itu abstraksi data.

Ide dasar konsep abstraksi data adalah memproteksi data dari pengaksesan secara langsung oleh program. Data hanya dapat diakses melalui fungsi-fungsi yang secara khusus disediakan untuk mengakses dan memanipulasinya.

Dengan terproteksinya data dari pengaksesan secara langsung oleh program, maka dapat kita katakan bahwa data tersembunyi dari program. Data menjadi abstrak. Dalam pandangan program, data bersifat abstrak – tidak kelihatan. Program tidak mengetahui bagaimana struktur datanya.

Program juga tidak mengetahui bagaimana data dimanipulasi. Satu-satunya yang diketahui program adalah bahwa untuk mengakses dan memanipulasi data, ia harus menggunakan fungsi-fungsi pemanipulasi data yang sudah disediakan objeknya.

Jadi dengan abstraksi data, program dan data menjadi dua bagian yang terpisah, yang masing-masing berdiri sendiri. Tidak ada keterikatan. Kumpulan data dan fungsi-fungsi pemanipulasi data yang bersifat abstrak tersebut dikenal dengan istilah tipe data abstrak (abstract data type, yang disingkat ADT). Proses pengumpulan data dan fungsi-fungsi pemanipulasinya dikenal dengan istilah enkapsulasi.

Dengan terpisahnya data dari program, maka kita dapat memperoleh tiga keuntungan. Yang pertama, perubahan struktur data tidak akan mempengaruhi program. Karena program tidak terikat dengan data, maka kita hanya perlu melakukan perubahan pada fungsi-fungsi pemanipulasi data. Kita tidak perlu memodifikasi program secara keseluruhan. Keuntungan yang kedua adalah program menjadi lebih terorganisir. Dan keuntungan yang ketiga adalah ADT dapat kita gunakan pada program-program lainnya, karena ia merupakan bagian yang berdiri sendiri yang tidak lagi terikat pada program. Inilah yang disebut sebagai bagian program (objek) yang reusable.

Nah, secara tidak langsung ketiga keuntungan tersebut telah menjawab pertanyaan mengapa abstraksi data penting dan perlu dilakukan. Bukti atau penjelasan yang konkret mengenai pentingnya abstraksi data dan bagaimana abstraksi data dilakukan dapat Anda simak pada bagian pembahasan contoh program TELEPON1.CPP sampai dengan TELEPON4.CPP.

Tapi sebelumnya, ada baiknya kita menyimak deskripsi programnya berikut ini:

Program TELEPON1.CPP-TELEPON4.CPP adalah program-program yang bertujuan menyimpan daftar nama dan nomor telpon teman atau kerabat kita. Untuk keperluan itu program memiliki fasilitas untuk menambah data ke dalam daftar, menampilkan daftar ke layar, dan mencari data dalam daftar. Program juga mampu menampilkan menu untuk untuk memudahkan pengguna melakukan apa yang diinginkannya.

Nah, setelah mengetahui deskripsi programnya, sekarang saatnya kita mengikuti jalannya evolusi abstraksi data dari program terstruktur ke program berorientasi objek dan sekaligus membuktikan apakah benar dengan abstraksi data, ketiga keuntungan di atas dapat kita peroleh.

Saya sarankan kepada Anda untuk membaca pembahasan program secara berurutan mulai dari program TELEPON1.CPP sampai TELEPON4.CPP. Karena dengan begitu Anda akan dapat merasakan evolusi yang terjadi. Saya juga menyarankan kepada Anda untuk mencoba sendiri contoh-contoh program yang ada, karena cara terbaik belajar pemrograman adalah dengan memprogram. Nah, selamat mengikuti!

TELEPON1.CPP
Mari kita simak listing program TELEPON1.CPP berikut ini.

/*
   TELEPON1.CPP
   Tanpa abstraksi data. Program menggunakan fungsi
   untuk memecah-mecah program dan mengurangi pengulangan
   kode program. Program terikat pada array global.

   Chandra MDE, 29 Januari 1999
   "Jalanin aja apa adanya, pokoknya berikan yang terbaik."
                                         – Whenyna E.R. –
*/

// INCLUDE //
#include <iostream.h>
#include <string.h>

// REKORD DATA //
typedef struct
{
   char nama[20];
   char telp[20];
} RekordTelepon;

const MAX = 10;  // jumlah data maksimal

// DEKLARASI VARIABEL DATA //
RekordTelepon list[MAX];
int counter;

// FUNGSI-FUNGSI PROGRAM UTAMA //
void tambahData()
{
   if (counter<MAX)
   {
      cout << endl;
      cout << "Masukkan nama: ";
      cin >> list[counter].nama;
      cout << "Nomor telepon: ";
      cin >> list[counter].telp;
      counter++;
   }
   else
   {
      cout << "List sudah penuh!\n";
   }
}

void tampilkanData(int i)
{
   cout << endl;
   cout << "Nama:    " << list[i].nama << endl;
   cout << "Telepon: " << list[i].telp << endl;
}

void tampilkanSemuaData()
{
   int i;

   if (counter>0)
      for (i=0; i<counter; i++)
         tampilkanData(i);
   else
      cout << "Belum ada data!\n";
   cout << endl;
}

void cariData()
{
   char s[20];
   int i, ketemu = 0;

   if (counter==0)
      cout << "Belum ada data!\n";
   else
   {
      cout << endl;
      cout << "Masukkan nama yang dicari: ";
      cin >> s;
      for (i=0; i<counter; i++)
      {
         if (strcmp(s, list[i].nama)==0)
         {
            tampilkanData(i);
            ketemu = 1;
         }
      }
      if (!ketemu)
         cout << "Tidak ketemu!\n";
   }
}

void tampilkanMenu()
{
   cout << endl;
   cout << "TELEPON1.CPP – Menu Utama\n";
   cout << "————————-\n";
   cout << "   1 – Tambah Data\n";
   cout << "   2 – Tampilkan Data\n";
   cout << "   3 – Cari Data\n";
   cout << "   4 – Keluar\n";
   cout << "————————-\n";
   cout << "Masukkan pilihan: ";
}

// PROGRAM UTAMA //
void main()
{
   char pil;
   int selesai;

   counter = 0; selesai = 0;
   while (!selesai)
   {
      tampilkanMenu();
      cin >> pil;
      switch(pil)
      {
         case ‘1’: tambahData();
                   break;
         case ‘2’: tampilkanSemuaData();
                   break;
         case ‘3’: cariData();
                   break;
         case ‘4’: cout << "Selesai\n";
                   selesai = 1;
                   break;
         default:  cout << "Pilih 1-4 saja!\n";
      }
   }
}

Program ini disusun dengan teknik pemrograman terstruktur. Fungsi digunakan untuk memecah program menjadi bagian-bagian yang lebih kecil. Satu bagian untuk menambah data ke dalam daftar (fungsi tambahData), satu bagian untuk menampilkan daftar ke layar (fungsi tampilkanSemuaData), satu bagian untuk mencari data dalam daftar (fungsi cariData), dan satu bagian untuk menampilkan menu utama program (fungsi tampilkanMenu).

Selain untuk memecah program, dalam program ini, fungsi juga digunakan untuk mengurangi terjadinya pengulangan kode program (redundancy removal). Fungsi tampilkanData yang terdapat dalam program digunakan untuk tujuan ini. Dalam program, fungsi ini digunakan oleh fungsi tampilkanSemuaData dan fungsi cariData.

Untuk menyimpan data, program menggunakan sebuah array global. Array ini diakses secara langsung oleh program melalui fungsi-fungsi yang telah disebutkan di atas kecuali fungsi tampilkanMenu. Data bersifat transparan. Tidak tersembunyi. Atau dengan kata lain, program TELEPON1.CPP ini tidak menerapkan konsep abstraksi data.

strukturprogramtelepon14 
Gambar 1. Struktur Program TELEPON1.CPP – TELEPON4.CPP

Sekarang mari kita perhatikan Gambar 1-A. Gambar ini menunjukkan struktur program TELEPON1.CPP. Dalam gambar tersebut nampak bahwa array bersifat global dan diakses secara langsung oleh kode program. Hal ini menyebabkan program terikat pada data. Dan ini bukanlah hal yang baik. Akibat adanya keterikatan ini, maka jika suatu saat kita ingin atau perlu mengganti struktur data array dengan struktur data yang lain (linked-list misalnya), kita harus menulis kembali sebagian besar program, bahkan hampir keseluruhan program. Tidak ada jalan lain, karena semua fungsi program mereferensi array secara langsung. Semua fungsi tersebut terikat pada array global. Dan inilah kelemahan program TELEPON1.CPP.

 image

Kelemahan ini dapat diatasi dengan menerapkan konsep abstraksi data pada program tersebut. Bagaimana hal itu dilakukan? Untuk mengetahuinya silakan Anda ikuti pembahasan program TELEPON2.CPP berikut ini.

TELEPON2.CPP
Program ini adalah sebuah contoh penerapan abstraksi data pada program yang disusun secara terstruktur. Kalau pada program TELEPON1.CPP Anda telah melihat penggunaan fungsi untuk memecah program dan mengurangi terjadinya pengulangan kode program, maka pada program TELEPON2.CPP ini Anda akan melihat penggunaan fungsi untuk memprotek data dari pengaksesan secara langsung oleh program.

Kita telah mengetahui bahwa ide dasar abstraksi data adalah memproteksi data dari pengaksesan secara langsung oleh program. Dengan demikian, untuk menerapkan abstraksi data dalam program, kita harus menyusun program sedemikian rupa, dengan fasilitas yang tersedia, sehingga program tidak perlu lagi mengakses data secara langsung. Dengan kata lain kita harus membuat data menjadi abstrak ditinjau dari ‘penglihatan’ program. Untuk dapat mengabstraksi data maka kita perlu membuat sebuah tipe data abstrak (ADT), yaitu kumpulan data dan fungsi-fungsi pemanipulasinya.

Marilah kita simak listing program TELEPON2.CPP berikut ini.

/*
   TELEPON2.CPP
   Contoh abstraksi data pada program terstruktur.
   Menggunakan fungsi sebagai "dinding pemisah" antara
   data dan program. Perubahan struktur data tidak
   berpengaruh pada seluruh program, hanya fungsi-fungsi
   pemanipulasi array yang harus diubah. Tapi data masih
   bisa diakses secara langsung oleh pemrogram.

   Chandra MDE, 30 Januari 1999
   "We have to be responsible for what we’ve said."
                                   – Whenyna E.R. –
*/

// INCLUDE //
#include <iostream.h>
#include <string.h>

// REKORD DATA //
typedef struct
{
   char nama[20];
   char telp[20];
} RekordTelepon;

const MAX = 10;  // jumlah data maksimal

// DEKLARASI VARIABEL DATA //
RekordTelepon list[MAX];
int counter;

// FUNGSI-FUNGSI PEMANIPULASI ARRAY //
void listMulai()
{
   counter = 0;
}

void listSelesai()
{
}

int listPenuh()
{
   if (counter>=MAX)
      return 1;
   else
      return 0;
}

int listKosong()
{
   if (counter==0)
      return 1;
   else
      return 0;
}

int listJumlahData()
{
   return counter;
}

int listTambahData(RekordTelepon RT)
{
   if (!listPenuh())
   {
      list[counter++] = RT;
      return 0;   // kembalikan 0 jika OK
   }
   return 1;
}

int listAmbilData(RekordTelepon& RT, int i)
{
   if (i<listJumlahData())
   {
      RT = list[i];
      return 0;   // kembalikan 0 jika OK
   }
   return 1;
}

// FUNGSI-FUNGSI PROGRAM UTAMA //
void tambahData()
{
   RekordTelepon RT;

   if (!listPenuh())
   {
      cout << endl;
      cout << "Masukkan nama: ";
      cin >> RT.nama;
      cout << "Nomor telepon: ";
      cin >> RT.telp;
      listTambahData(RT);
   }
   else
   {
      cout << "List sudah penuh!\n";
   }
}

void tampilkanData(RekordTelepon RT)
{
   cout << endl;
   cout << "Nama:    " << RT.nama << endl;
   cout << "Telepon: " << RT.telp << endl;
}

void tampilkanSemuaData()
{
   int i;
   RekordTelepon RT;

   if (!listKosong())
   {
      for (i=0; i<listJumlahData(); i++)
      {
         listAmbilData(RT, i);
         tampilkanData(RT);
      }
   }
   else
   {
      cout << "Belum ada data!\n";
   }
   cout << endl;
}

void cariData()
{
   char s[20];
   int i, ketemu = 0;
   RekordTelepon RT;

   if (listJumlahData()==0)
      cout << "Belum ada data!\n";
   else
   {
      cout << endl;
      cout << "Masukkan nama yang dicari: ";
      cin >> s;
      for (i=0; i<listJumlahData(); i++)
      {
         listAmbilData(RT, i);
         if (strcmp(s, RT.nama)==0)
         {
            tampilkanData(RT);
            ketemu = 1;
         }
      }
      if (!ketemu)
         cout << "Tidak ketemu!\n";
   }
}

void tampilkanMenu()
{
   cout << endl;
   cout << "TELEPON2.CPP – Menu Utama\n";
   cout << "————————-\n";
   cout << "   1 – Tambah Data\n";
   cout << "   2 – Tampilkan Data\n";
   cout << "   3 – Cari Data\n";
   cout << "   4 – Keluar\n";
   cout << "————————-\n";
   cout << "Masukkan pilihan: ";
}

// PROGRAM UTAMA //
void main()
{
   char pil;
   int selesai;

   listMulai();
   while (!selesai)
   {
      tampilkanMenu();
      cin >> pil;
      switch(pil)
      {
         case ‘1’: tambahData();
                   break;
         case ‘2’: tampilkanSemuaData();
                   break;
         case ‘3’: cariData();
                   break;
         case ‘4’: cout << "Selesai\n";
                   selesai = 1;
                   break;
         default:  cout << "Pilih 1-4 saja!\n";
      }
   }
}

Pada TELEPON2.CPP terdapat tujuh buah fungsi berawalan list. Dengan ketujuh fungsi tersebut, program dapat mengakses dan memanipulasi data tanpa perlu mengakses data secara langsung. Dalam hal ini ketujuh fungsi tersebut berfungsi sebagai ‘dinding pemisah’ yang memproteksi data dari pengaksesan secara langsung oleh program.

Struktur program TELEPON2.CPP dapat Anda lihat pada Gambar 1-B. Terlihat di sana bahwa array global tidak lagi diakses secara langsung oleh kode program, tetapi diakses melalui fungsi-fungsi array, yaitu tujuh buah fungsi berawalan list tersebut. Dengan struktur program semacam ini, perubahan struktur data tidak akan mempengaruhi kode program. Hanya ketujuh fungsi pemanipulasi array yang perlu dimodifikasi.

Sampai di sini, penggunaan fungsi sebagai ‘dinding pemisah’ sepertinya telah berhasil memprotek array global dari pengaksesan secara langsung oleh program. Namun, benarkah demikian? Tidak! Keberadaan ketujuh fungsi tersebut tidak menjamin tidak terjadinya pengaksesan data secara langsung oleh program. Bisa saja seorang pemrogram yang kurang disiplin datang, lalu memodifikasi program, mengakses array secara langsung, dan menerjang ‘dinding pemisah’ yang telah kita bangun. Tidak adanya jaminan ini adalah kelemahan yang terdapat pada program TELEPON2.CPP.

image

Dapatkah kelemahan tersebut dihilangkan? Tentu saja. Dengan menggunakan teknik pemrograman berorientasi objek, maka kelemahan tersebut dapat dihilangkan dengan mudah.

TELEPON3.CPP
Kelemahan yang terdapat dalam program TELEPON2.CPP menunjukkan bahwa dengan teknik pemrograman terstruktur, abstraksi data tidak dapat sepenuhnya diterapkan dalam program. Kita tidak dapat sepenuhnya memproteksi data dari pengaksesan data secara langsung oleh program. Kita tidak dapat memaksa pemrogram untuk tidak mengakses data secara langsung. Pada bagian ini Anda akan melihat bahwa dengan fasilitas pemrograman berorientasi objek yang dimiliki C++, kekurangan yang ada pada program sebelumnya dapat diatasi dengan mudah.

/*
   TELEPON3.CPP
   Menggunakan kelas untuk membuat tipe data abstrak.
   Data private sepenuhnya terprotek dari pengaksesan
   secara langsung oleh program. Program tidak terikat
   pada data. Perubahan struktur data tidak berpengaruh
   pada program, hanya kelas (ADT) yang harus diubah.

   Chandra MDE, 5 Februari 1999
   "S i n a u  s i n g  t e n a n a n !"
                        – Whenyna E.R. –
*/

// INCLUDE //
#include <iostream>
#include <string.h>

// REKORD DATA //
typedef struct
{
   char nama[20];
   char telp[20];
} RekordTelepon;

#define MAX 10  // jumlah data maksimum

// KELAS List //
class List
{
//private:  // data tersembunyi
   RekordTelepon list[MAX];
   int counter;

public:  // fungsi-fungsi pemanipulasi
   List();
   ~List();
   int Penuh();
   int Kosong();
   int JumlahData();
   int TambahData(RekordTelepon RT);
   int AmbilData(RekordTelepon& RT, int i);
};

// IMPLEMENTASI KELAS List //
   List::List()    // constructor
   {
      counter = 0;
   }

   List::~List()  // destructor
   {
   }

   int List::Penuh()
   {
      if (counter>=MAX)
         return 1;
      else
         return 0;
   }

   int List::Kosong()
   {
      if (counter==0)
         return 1;
      else
         return 0;
   }

   int List::JumlahData()
   {
      return counter;
   }

   int List::TambahData(RekordTelepon RT)
   {
      if (!Penuh())
      {
         list[counter++] = RT;
         return 0;   // kembalikan 0 jika OK
      }
      return 1;
   }

   int List::AmbilData(RekordTelepon& RT, int i)
   {  
      if (i<JumlahData())
      {
         RT = list[i];
         return 0;   // kembalikan 0 jika OK
      }
      return 1;
   }

// DEKLARASI VARIABEL DATA //
   List list;  // Deklarasi ini secara otomatis memanggil
               // constructor List()

// FUNGSI-FUNGSI PROGRAM UTAMA //
void tambahData()
{
   RekordTelepon RT;

   if (!list.Penuh())
   {
      cout << endl;
      cout << "Masukkan nama: ";
      cin >> RT.nama;
      cout << "Nomor telepon: ";
      cin >> RT.telp;
      list.TambahData(RT);
   }
   else
   {
      cout << "List sudah penuh!\n";
   }
}

void tampilkanData(RekordTelepon RT)
{
   cout << endl;
   cout << "Nama:    " << RT.nama << endl;
   cout << "Telepon: " << RT.telp << endl;
}

void tampilkanSemuaData()
{
   int i;
   RekordTelepon RT;

   if (!list.Kosong())
   {
      for (i=0; i<list.JumlahData(); i++)
      {
         list.AmbilData(RT, i);
         tampilkanData(RT);
      }
   }
   else
   {
      cout << "Belum ada data!\n";
   }
   cout << endl;
}

void cariData()
{
   char s[20];
   int i, ketemu = 0;
   RekordTelepon RT;

   if (list.JumlahData()==0)
      cout << "Belum ada data!\n";
   else
   {
      cout << endl;
      cout << "Masukkan nama yang dicari: ";
      cin >> s;
      for (i=0; i<list.JumlahData(); i++)
      {
         list.AmbilData(RT, i);
         if (strcmp(s, RT.nama)==0)
         {
            tampilkanData(RT);
            ketemu = 1;
         }
      }
      if (!ketemu)
         cout << "Tidak ketemu!\n";
   }
}

void tampilkanMenu()
{
   cout << endl;
   cout << "TELEPON3.CPP – Menu Utama\n";
   cout << "————————-\n";
   cout << "   1 – Tambah Data\n";
   cout << "   2 – Tampilkan Data\n";
   cout << "   3 – Cari Data\n";
   cout << "   4 – Keluar\n";
   cout << "————————-\n";
   cout << "Masukkan pilihan: ";
}

// PROGRAM UTAMA //
void main()
{
   char pil;
   int selesai = 0;

   while (!selesai)
   {
      tampilkanMenu();
      cin >> pil;
      switch(pil)
      {
         case ‘1’: tambahData();
                   break;
         case ‘2’: tampilkanSemuaData();
                   break;
         case ‘3’: cariData();
                   break;
         case ‘4’: cout << "Selesai\n";
                   selesai = 1;
                   break;
         default:  cout << "Pilih 1-4 saja!\n";
      }
   }
}

Program TELEPON3.CPP tidak lagi disusun dengan teknik pemrograman terstruktur. Program ini disusun dengan teknik pemrograman berorientasi objek. Tipe data abstrak dibuat dengan kelas (class), yaitu salah satu fasilitas yang disediakan oleh bahasa C++.

Kelas adalah pengembangan dari struct. Kalau struct berisi sekumpulan data saja, maka kelas dapat berisi sekumpulan data  dan sekumpulan fungsi pemanipulasi data. Data biasanya bersifat private dan fungsi pemanipulasi data yang biasanya bersifat public. Dengan kelas C++, maka abstraksi data dapat dilakukan dengan mudah. Dan dijamin, data yang private tidak dapat diakses secara langsung oleh program. Program SCOPE.CPP telah membuktikan hal ini.

Mari kita perhatikan listing program TELEPON3.CPP berikut ini. Di situ dapat kita lihat bahwa List didefinisikan sebagai sebuah variabel kelas. Dalam kelas tersebut dideklarasikan sebuah array penyimpan data dan sebuah variabel integer yang bersifat private. Kedua variabel tersebut sepenuhnya tersembunyi dari program. Array hanya dapat diakses melalui fungsi-fungsi anggota kelas tersebut. Dalam kelas tersebut juga didefinisikan tujuh buah fungsi pemanipulasi array yang kodenya 100% sama dengan ketujuh buah fungsi pada program TELEPON2.CPP.

Sekarang perhatikan Gambar 1-B dan 1-C. Pada Gambar 1-B, array global dan fungsi-fungsi array masih terpisah. Ini menunjukkan bahwa abstraksi data belum sepenuhnya berhasil diimplementasikan. Lain halnya dengan Gambar 1-C. Pada Gambar 1-C ditunjukkan bahwa data (array) private dan fungsi-fungsi public merupakan satu kesatuan. Hal ini ditunjukkan dengan adanya garis tebal yang mengelilingi kedua blok tersebut. Blok besar yang tersusun dari blok data dan fungsi tersebut disebut sebagai objek. Atau dalam bahasa C++ disebut sebagai kelas. Inilah tipe data abstrak yang sebenarnya.

Nah, sampai di sini Anda telah melihat bahwa dengan pemrograman berorientasi objek, proses abstraksi data atau enkapsulasi data dapat kita lakukan dengan mudah melalui penggunaan kelas. Anda juga telah melihat bahwa dengan kelas, data benar-benar tersembunyi dari program. Dinding pemisah antara program dan data tidak dapat lagi diterjang oleh pemrogram yang suka nyelonong.

image

TELEPON4.CPP
Dalam pembahasan program TELEPON1.CPP-TELEPON3.CPP, Anda telah melihat evolusi abstraksi data dari program terstruktur ke program berorientasi objek. Sampai di sini mungkin Anda telah dapat merasakan efek positif dari penerapan abstraksi data dalam program. Tepat sekali! Program menjadi lebih terorganisir. Kalau Anda masih ingat, ini adalah keuntungan kedua dari penerapan abstraksi data dalam program.

Tetapi bagaimana dengan keuntungan yang pertama dan keuntungan yang ketiga? Apakah benar dengan abstraksi data perubahan struktur data tidak mempengaruhi kode program dan hanya perlu memodifikasi ADT? Juga, apakah benar ADT bersifat reusable? Dapatkah ADT diterapkan untuk program yang lain?

Program TELEPON4.CPP akan menunjukkan kepada Anda bahwa perubahan struktur data tidak mempengaruhi kode program. Perubahan struktur data hanya mempengaruhi ADT.

Mari kita simak listing program TELEPON4.CPP berikut ini. Program ini adalah hasil pemodifikasian program TELEPON3.CPP. Program ini tidak lagi menggunakan struktur data array untuk menyimpan data. Program menggunakan struktur data singly linked-list. Bagi Anda yang masih awam dengan struktur data ini, Anda dapat mempelajarinya dari buku-buku pemrograman yang membahas tentang struktur data dan algoritmanya.

/*
   TELEPON4.CPP
   Sama seperti TELEPON3.CPP, hanya saja struktur data array
   diganti dengan single linked-list. Tidak ada perubahan
   pada kode program. Hanya kelas List yang diubah.
   Inilah pentingnya abstraksi data!

   Chandra MDE, 5 Februari 1999
   I dedicate this article to my love, Whenyna Eka Rini.
*/

// INCLUDE //
#include <iostream.h>
#include <string.h>

// REKORD DATA //
typedef struct
{
   char nama[20];
   char telp[20];
} RekordTelepon;

struct RekordPtr
{
   RekordTelepon data;
   RekordPtr* next;
};

#define MAX 10  // jumlah data maksimal

// KELAS List //
class List
{
private:  // data tersembunyi
   RekordPtr* list;
   int counter;

public:  // fungsi-fungsi pemanipulasi
   List();
   ~List();
   int Penuh();
   int Kosong();
   int JumlahData();
   int TambahData(RekordTelepon RT);
   int AmbilData(RekordTelepon& RT, int i);
   void Del();
   void Release();
};

// IMPLEMENTASI KELAS List //
   List::List()  // constructor
   {
      list = 0;
      counter = 0;
   }

   List::~List()  // destructor
   {
      Release();  // bebaskan memori
   }

   int List::Penuh()
   {
      if (counter>=MAX)
         return 1;
      else
         return 0;
   }

   int List::Kosong()
   {
      if (counter==0)
         return 1;
      else
         return 0;
   }

   int List::JumlahData()
   {
      return counter;
   }

   int List::TambahData(RekordTelepon RT)
   {
      if (!Penuh())
      {
         RekordPtr* temp;

         temp = new RekordPtr;
         temp->next = list;
         temp->data = RT;
         list = temp;
         counter++;
         return 0;   // kembalikan 0 jika OK
      }
      return 1;
   }

   int List::AmbilData(RekordTelepon& RT, int i)
   {
      int a = JumlahData()-1;
      RekordPtr* temp = list;

      if (i<JumlahData())
      {
         while (temp!=0)
         {
            RT = temp->data;
            if (a==i) return 0; // kembalikan 0 jika OK
            temp = temp->next;
            a–;
         }
      }
      return 1;
   }

   void List::Del()
   {
      RekordPtr* temp = list;

      list = list->next;
      delete temp;
   }

   void List::Release()
   {
      while (list != 0)
         Del();
   }

// DEKLARASI VARIABEL DATA //
   List list;  // Deklarasi ini secara otomatis memanggil
               // constructor List()

// FUNGSI-FUNGSI PROGRAM UTAMA //
void tambahData()
{
   RekordTelepon RT;

   if (!list.Penuh())
   {
      cout << endl;
      cout << "Masukkan nama: ";
      cin >> RT.nama;
      cout << "Nomor telepon: ";
      cin >> RT.telp;
      list.TambahData(RT);
   }
   else
   {
      cout << "List sudah penuh!\n";
   }
}

void tampilkanData(RekordTelepon RT)
{
   cout << endl;
   cout << "Nama:    " << RT.nama << endl;
   cout << "Telepon: " << RT.telp << endl;
}

void tampilkanSemuaData()
{
   int i;
   RekordTelepon RT;

   if (!list.Kosong())
   {
      for (i=0; i<list.JumlahData(); i++)
      {
         list.AmbilData(RT, i);
         tampilkanData(RT);
      }
   }
   else
   {
      cout << "Belum ada data!\n";
   }
   cout << endl;
}

void cariData()
{
   char s[20];
   int i, ketemu = 0;
   RekordTelepon RT;

   if (list.JumlahData()==0)
      cout << "Belum ada data!\n";
   else
   {
      cout << endl;
      cout << "Masukkan nama yang dicari: ";
      cin >> s;
      for (i=0; i<list.JumlahData(); i++)
      {
         list.AmbilData(RT, i);
         if (strcmp(s, RT.nama)==0)
         {
            tampilkanData(RT);
            ketemu = 1;
         }
      }
      if (!ketemu)
         cout << "Tidak ketemu!\n";
   }
}

void tampilkanMenu()
{
   cout << endl;
   cout << "TELEPON4.CPP – Menu Utama\n";
   cout << "————————-\n";
   cout << "   1 – Tambah Data\n";
   cout << "   2 – Tampilkan Data\n";
   cout << "   3 – Cari Data\n";
   cout << "   4 – Keluar\n";
   cout << "————————-\n";
   cout << "Masukkan pilihan: ";
}

// PROGRAM UTAMA //
void main()
{
   char pil;
   int selesai = 0;

   while (!selesai)
   {
      tampilkanMenu();
      cin >> pil;
      switch(pil)
      {
         case ‘1’: tambahData();
                   break;
         case ‘2’: tampilkanSemuaData();
                   break;
         case ‘3’: cariData();
                   break;
         case ‘4’: cout << "Selesai\n";
                   selesai = 1;
                   break;
         default:  cout << "Pilih 1-4 saja!\n";
      }
   }
}

Dalam listing tersebut dapat Anda lihat bahwa kode program, dimulai dari pendeklarasian variabel data list, tidak mengalami perubahan. Hanya kelas List saja yang mengalami perubahan. Fungsi tambahData, tampilkanSemuaData, cariData dan tampilkanData tidak mengalami perubahan sedikitpun. Inilah bukti bahwa dengan abstraksi data, perubahan struktur data hanya mempengaruhi tipe data abstrak saja. Kita tidak perlu memodifikasi kode program.

image

Selanjutnya, untuk mengetahui apakah ADT bersifat reusable, perhatikan ilustrasi berikut ini. Misalkan Anda ingin membuat sebuah program untuk menyimpan data karyawan dengan field-field data berupa nama, alamat, jenis kelamin, dan sebagainya. Maka untuk keperluan ini, Anda dapat menggunakan ADT, baik ADT dari program TELEPON2.CPP maupun ADT dari program TELEPON3.CPP.

Kalau Anda menggunakan teknik pemrograman terstruktur, maka Anda gunakan ADT dari program TELEPON2.CPP. Dan kalau Anda menggunakan teknik pemrograman berorientasi objek, maka gunakanlah ADT (kelas) dari program TELEPON3.CPP. Yang perlu Anda lakukan hanyalah mengubah definisi rekord pada bagian awal program. Fungsi-fungsi pemanipulasi data dapat langsung Anda gunakan dalam program. Namun, tentu saja Anda juga harus memodifikasi fungsi-fungsi pada kode program untuk disesuaikan dengan rekord data yang baru.

Nah, itulah akhir dari tulisan ini. Semoga apa yang telah saya sampaikan di sini dapat bermanfaat bagi kita semua.

Kepustakaan
"Understanding C++: An Accelerated Introduction", Marshall Brain & Kelly Campbell. 1998.
"Turbo C++", Ira Pohl. Benjamin/Cummings Publishing Company, Inc.

-oOo-

Catatan Penting!
Jika Anda menggunakan kompiler C++ seperti G++ atau yang lain yang menggunakan standard C++11 dan bukan menggunakan interpreter Ch, maka terdapat beberapa perubahan pokok yang harus dibuat antara lain:

  1. Ganti #include <iostream.h>; dengan #include <iostream>
  2. Tambahkan using namespace std;
  3. Ganti fungsi utama void main {} menjadi int main() { return 0; }

Untuk lebih amannya, berikut saya berikan listing program TELEPON4.CPP versi C++11.

/*
   TELEPON4.CPP
   Sama seperti TELEPON3.CPP, hanya saja struktur data array
   diganti dengan single linked-list. Tidak ada perubahan
   pada kode program. Hanya kelas List yang diubah.
   Inilah pentingnya abstraksi data!

   Chandra MDE, 5 Februari 1999
   I dedicate this article to my love, Whenyna Eka Rini.
*/

// INCLUDE //
#include <iostream.h>
#include <string.h>

// REKORD DATA //
typedef struct
{
   char nama[20];
   char telp[20];
} RekordTelepon;

struct RekordPtr
{
   RekordTelepon data;
   RekordPtr* next;
};

#define MAX 10  // jumlah data maksimal

// KELAS List //
class List
{
private:  // data tersembunyi
   RekordPtr* list;
   int counter;

public:  // fungsi-fungsi pemanipulasi
   List();
   ~List();
   int Penuh();
   int Kosong();
   int JumlahData();
   int TambahData(RekordTelepon RT);
   int AmbilData(RekordTelepon& RT, int i);
   void Del();
   void Release();
};

// IMPLEMENTASI KELAS List //
   List::List()  // constructor
   {
      list = 0;
      counter = 0;
   }

   List::~List()  // destructor
   {
      Release();  // bebaskan memori
   }

   int List::Penuh()
   {
      if (counter>=MAX)
         return 1;
      else
         return 0;
   }

   int List::Kosong()
   {
      if (counter==0)
         return 1;
      else
         return 0;
   }

   int List::JumlahData()
   {
      return counter;
   }

   int List::TambahData(RekordTelepon RT)
   {
      if (!Penuh())
      {
         RekordPtr* temp;

         temp = new RekordPtr;
         temp->next = list;
         temp->data = RT;
         list = temp;
         counter++;
         return 0;   // kembalikan 0 jika OK
      }
      return 1;
   }

   int List::AmbilData(RekordTelepon& RT, int i)
   {
      int a = JumlahData()-1;
      RekordPtr* temp = list;

      if (i<JumlahData())
      {
         while (temp!=0)
         {
            RT = temp->data;
            if (a==i) return 0; // kembalikan 0 jika OK
            temp = temp->next;
            a–;
         }
      }
      return 1;
   }

   void List::Del()
   {
      RekordPtr* temp = list;

      list = list->next;
      delete temp;
   }

   void List::Release()
   {
      while (list != 0)
         Del();
   }

// DEKLARASI VARIABEL DATA //
   List list;  // Deklarasi ini secara otomatis memanggil
               // constructor List()

// FUNGSI-FUNGSI PROGRAM UTAMA //
void tambahData()
{
   RekordTelepon RT;

   if (!list.Penuh())
   {
      cout << endl;
      cout << "Masukkan nama: ";
      cin >> RT.nama;
      cout << "Nomor telepon: ";
      cin >> RT.telp;
      list.TambahData(RT);
   }
   else
   {
      cout << "List sudah penuh!\n";
   }
}

void tampilkanData(RekordTelepon RT)
{
   cout << endl;
   cout << "Nama:    " << RT.nama << endl;
   cout << "Telepon: " << RT.telp << endl;
}

void tampilkanSemuaData()
{
   int i;
   RekordTelepon RT;

   if (!list.Kosong())
   {
      for (i=0; i<list.JumlahData(); i++)
      {
         list.AmbilData(RT, i);
         tampilkanData(RT);
      }
   }
   else
   {
      cout << "Belum ada data!\n";
   }
   cout << endl;
}

void cariData()
{
   char s[20];
   int i, ketemu = 0;
   RekordTelepon RT;

   if (list.JumlahData()==0)
      cout << "Belum ada data!\n";
   else
   {
      cout << endl;
      cout << "Masukkan nama yang dicari: ";
      cin >> s;
      for (i=0; i<list.JumlahData(); i++)
      {
         list.AmbilData(RT, i);
         if (strcmp(s, RT.nama)==0)
         {
            tampilkanData(RT);
            ketemu = 1;
         }
      }
      if (!ketemu)
         cout << "Tidak ketemu!\n";
   }
}

void tampilkanMenu()
{
   cout << endl;
   cout << "TELEPON4.CPP – Menu Utama\n";
   cout << "————————-\n";
   cout << "   1 – Tambah Data\n";
   cout << "   2 – Tampilkan Data\n";
   cout << "   3 – Cari Data\n";
   cout << "   4 – Keluar\n";
   cout << "————————-\n";
   cout << "Masukkan pilihan: ";
}

// PROGRAM UTAMA //
void main()
{
   char pil;
   int selesai = 0;

   while (!selesai)
   {
      tampilkanMenu();
      cin >> pil;
      switch(pil)
      {
         case ‘1’: tambahData();
                   break;
         case ‘2’: tampilkanSemuaData();
                   break;
         case ‘3’: cariData();
                   break;
         case ‘4’: cout << "Selesai\n";
                   selesai = 1;
                   break;
         default:  cout << "Pilih 1-4 saja!\n";
      }
   }
}

Nah, jika Anda menginginkan file-file program SCOPE.CPP, TELEPON1.CPP, dan yang lainnya, silakan menuliskan komentar di bawah postingan ini beserta alamat email Anda. Saya akan mengirimkan file-file tersebut dalam bentuk file kompresi langsung ke alamat email Anda.

Selamat belajar dan selamat berkarya!

😀