Spesifikasi dan badan

Pemisahan spesifikasi dan badan package serta ketergantungan klien pada spesifikasi.

Daftar isi

Sebuah package di Ada umumnya terdiri atas dua unit kompilasi yang dipisahkan secara tegas, yaitu spesifikasi (spec) yang memuat antarmuka dan badan (body) yang memuat implementasi. Topik ini membahas susunan kedua unit tersebut, cara kompilasi program multi-berkas dengan gnatmake, serta hubungan ketergantungan antara klien dan spesifikasi. Setelah menyelesaikan topik ini, pemisahan package menjadi spec dan body dapat dilakukan sendiri, dan alasan di balik jumlah berkas yang dikompilasi ulang oleh gnatmake dapat dipahami dari keluarannya.

Spesifikasi sebagai antarmuka#

Spesifikasi package adalah unit kompilasi yang memuat deklarasi antarmuka, yaitu tipe, konstanta, dan subprogram yang boleh digunakan oleh unit lain. GNAT mengikat nama unit kepada nama berkas, sehingga spesifikasi package Konverter_Suhu wajib berada di berkas konverter_suhu.ads. Spesifikasi berikut hanya menjanjikan dua fungsi konversi suhu beserta kontrak parameternya, tanpa satu baris implementasi pun.

ada konverter_suhu.ads
package Konverter_Suhu is

   --  Antarmuka yang dilihat klien: hanya nama subprogram dan kontraknya.
   function Celsius_Ke_Fahrenheit (C : Float) return Float;
   function Fahrenheit_Ke_Celsius (F : Float) return Float;

end Konverter_Suhu;

Yang diekspor kepada unit lain hanya dua fungsi tersebut. Seluruh kontrak fungsi terlihat, yaitu nama fungsi, tipe parameter, dan tipe hasil. Informasi tersebut cukup bagi kompilator untuk memeriksa pemanggilan dari unit lain, sedangkan isi perhitungan tidak pernah diketahui oleh klien. Penyusunan spesifikasi dengan cara ini menghasilkan antarmuka yang ringkas dan mudah ditinjau.

Badan sebagai implementasi#

Badan package mengimplementasikan seluruh subprogram yang dijanjikan spesifikasinya dan berada di berkas konverter_suhu.adb. Apabila ada subprogram yang dideklarasikan pada spesifikasi tetapi tidak memiliki implementasi, program akan gagal pada tahap penyambungan (link). Selain implementasi tersebut, badan boleh memuat deklarasi tambahan yang tidak ada di spesifikasi.

ada konverter_suhu.adb
package body Konverter_Suhu is

   --  Subprogram bantu yang tidak diekspor: hanya terlihat di dalam badan.
   function Bulatkan (Nilai : Float) return Float is
   begin
      return Float'Rounding (Nilai * 10.0) / 10.0;
   end Bulatkan;

   function Celsius_Ke_Fahrenheit (C : Float) return Float is
      F : constant Float := C * 9.0 / 5.0 + 32.0;
   begin
      return Bulatkan (F);
   end Celsius_Ke_Fahrenheit;

   function Fahrenheit_Ke_Celsius (F : Float) return Float is
      C : constant Float := (F - 32.0) * 5.0 / 9.0;
   begin
      return Bulatkan (C);
   end Fahrenheit_Ke_Celsius;

end Konverter_Suhu;

Fungsi Bulatkan hanya dideklarasikan di dalam badan, sehingga fungsi itu hanya dapat dipanggil dari dalam badan yang sama. Deklarasi semacam ini digunakan untuk perhitungan internal yang tidak ditujukan kepada klien. GNAT menegakkan pemisahan tersebut pada saat kompilasi, sebagaimana diperlihatkan pada bagian Kesalahan umum di bawah.

Program klien dan kompilasi#

Program utama berperan sebagai klien, yaitu unit yang menggunakan package melalui klausa with. Klausa with menjadikan nama package dapat dirujuk, sedangkan pemanggilan subprogram ditulis dengan nama package sebagai awalan. Program berikut memanggil kedua fungsi konversi dan menampilkan hasilnya dengan satu angka desimal.

ada uji_konversi.adb
with Ada.Text_IO;
with Ada.Float_Text_IO;
with Konverter_Suhu;

procedure Uji_Konversi is

   procedure Tampilkan (Keterangan : String; Nilai : Float) is
   begin
      Ada.Text_IO.Put (Keterangan);
      Ada.Float_Text_IO.Put (Nilai, Fore => 1, Aft => 1, Exp => 0);
      Ada.Text_IO.New_Line;
   end Tampilkan;

begin
   Tampilkan ("25.0 F  = ", Konverter_Suhu.Fahrenheit_Ke_Celsius (25.0));
   Tampilkan ("-40.0 C = ", Konverter_Suhu.Celsius_Ke_Fahrenheit (-40.0));
   Tampilkan ("36.6 C  = ", Konverter_Suhu.Celsius_Ke_Fahrenheit (36.6));
end Uji_Konversi;

Kompilasi cukup dilakukan dengan menyebut berkas program utama. Perintah gnatmake menemukan spec dan body yang dirujuk secara otomatis berdasarkan nama unit, lalu mengompilasi seluruh unit yang dibutuhkan, menjalankan gnatbind, dan menautkan hasilnya dengan gnatlink.

bash
# kompilasi seluruh program dari direktori yang memuat berkas-berkasnya
gnatmake uji_konversi.adb
text Keluaran
gcc -c uji_konversi.adb
gcc -c konverter_suhu.adb
gnatbind -x uji_konversi.ali
gnatlink uji_konversi.ali
bash
# jalankan program hasil kompilasi
./uji_konversi
text Keluaran
25.0 F  = -3.9
-40.0 C = -40.0
36.6 C  = 97.9

Keluaran menunjukkan bahwa tiga nilai uji dikonversi dengan benar, termasuk suhu -40.0 derajat yang bernilai sama pada kedua skala. Tidak ada perintah tambahan yang diperlukan untuk memberi tahu gnatmake mengenai konverter_suhu.ads atau konverter_suhu.adb, karena pencariannya murni mengikuti nama unit yang disebut pada klausa with.

Ketergantungan klien pada spesifikasi#

gnatmake menyimpan jejak kompilasi pada berkas .ali dan mencatat dependensi setiap unit di dalamnya. Pada kompilasi berikutnya, gnatmake hanya mengompilasi ulang unit yang berkas sumbernya lebih baru daripada jejak pada berkas .ali, atau unit yang bergantung pada berkas yang berubah. Perilaku ini dapat diamati langsung dengan memodifikasi berkas dan mengompilasi ulang.

bash
# simulasikan perubahan pada berkas badan
touch konverter_suhu.adb
# kompilasi ulang program
gnatmake uji_konversi.adb
text Keluaran
gcc -c konverter_suhu.adb
gnatbind -x uji_konversi.ali
gnatlink uji_konversi.ali

Hanya badan package yang dikompilasi ulang, sementara program utama tidak tersentuh. Kontrak yang dilihat klien tidak berubah sama sekali, sehingga program klien tidak perlu diperiksa kembali. Inilah keuntungan utama pemisahan antarmuka dan implementasi, yaitu perbaikan internal dapat dilakukan tanpa mengganggu klien package.

bash
# simulasikan perubahan pada berkas spesifikasi
touch konverter_suhu.ads
# kompilasi ulang program
gnatmake uji_konversi.adb
text Keluaran
gcc -c uji_konversi.adb
gcc -c konverter_suhu.adb
gnatbind -x uji_konversi.ali
gnatlink uji_konversi.ali

Keadaan berbalik ketika berkas spesifikasi berubah. Karena panggilan pada program klien diperiksa terhadap spesifikasi, perubahan spesifikasi memicu kompilasi ulang klien dan badan sekaligus. Spesifikasi dibaca oleh semua klien, sehingga kualitas rancangan spesifikasi menentukan seberapa sering kompilasi ulang berskala besar terjadi.

Kesalahan umum#

Dua kesalahan berikut sering muncul ketika pemisahan spec dan body baru dipraktikkan, yaitu memanggil subprogram yang hanya ada di badan dan menulis badan sebelum spesifikasinya. Keduanya menghasilkan pesan error kompilasi yang spesifik, sehingga penyebabnya mudah dilacak.

Kesalahan pertama adalah memanggil subprogram yang dideklarasikan hanya di dalam badan. Deklarasi pada badan bukan bagian dari antarmuka, sehingga nama tersebut tidak dikenal di luar package.

ada uji_helper_error.adb
with Konverter_Suhu;

--  Berkas ini sengaja salah: memanggil subprogram yang hanya ada di badan.
procedure Uji_Helper_Error is
   Hasil : Float;
begin
   Hasil := Konverter_Suhu.Bulatkan (25.5);
end Uji_Helper_Error;
bash
# kompilasi berkas klien yang memanggil subprogram tersembunyi
gnatmake uji_helper_error.adb
text Keluaran
gcc -c uji_helper_error.adb
uji_helper_error.adb:7:27: error: "Bulatkan" not declared in "Konverter_Suhu"
gnatmake: "uji_helper_error.adb" compilation error

Pesan error menyatakan bahwa Bulatkan tidak dideklarasikan di Konverter_Suhu, dan pernyataan itu benar dari sudut pandang klien. Perbaikannya bergantung pada maksud desain. Apabila fungsi tersebut memang dimaksudkan untuk klien, pindahkan deklarasinya ke spesifikasi dan biarkan implementasinya di badan. Apabila fungsi tersebut bersifat internal, panggil hanya dari dalam badan.

Kesalahan kedua adalah menulis badan tanpa spesifikasinya. GNAT selalu mencari berkas spesifikasi terlebih dahulu ketika menemukan sebuah package body.

ada bodi_yatim.adb
package body Bodi_Yatim is

   --  Berkas ini sengaja salah: badan tanpa spesifikasi.
   procedure Tampilkan is
   begin
      null;
   end Tampilkan;

end Bodi_Yatim;
bash
# kompilasi badan yang tidak memiliki spesifikasi
gcc -c bodi_yatim.adb
text Keluaran
bodi_yatim.adb:1:14: error: file "bodi_yatim.ads" not found

Pesan error menyebut berkas bodi_yatim.ads yang tidak ditemukan, karena setiap package body wajib didahului oleh spesifikasinya. Perbaikannya adalah menulis spesifikasi Bodi_Yatim lebih dahulu, lengkap dengan deklarasi Tampilkan, sebelum badannya dikompilasi.

Ringkasan#

  • Setiap package terdiri atas spesifikasi (berkas .ads, memuat antarmuka) dan badan (berkas .adb, memuat implementasi), dan gnatmake menemukan keduanya secara otomatis dari nama unit.
  • Klien hanya bergantung pada spesifikasi. Perubahan badan tidak mengompilasi ulang klien, sedangkan perubahan spesifikasi mengompilasi ulang klien dan badan sekaligus.
  • Deklarasi yang hanya ada di dalam badan tidak terlihat dari luar, sehingga detail implementasi dapat disembunyikan tanpa alat tambahan.

Referensi#