Unit anak (child unit) adalah package yang dideklarasikan sebagai anak langsung dari package lain, misalnya Jadwal_Piket.Laporan yang menjadi anak dari Jadwal_Piket. Struktur ini membangun hierarki unit yang teratur pada program besar, dengan package induk sebagai akar dan unit anak sebagai perluasannya. Topik ini membahas pembentukan unit anak, hak akses istimewa terhadap bagian privat induk, serta cara klien menggunakannya. Contoh yang digunakan adalah package penjadwalan piket yang diperluas dengan unit anak pelaporan.
Package induk yang menjadi akar#
Package induk memuat tipe dan operasi inti, sedangkan perluasan ditempatkan pada unit anak. Spesifikasi berikut mendeklarasikan package Jadwal_Piket yang menyimpan penugasan piket untuk lima hari kerja. Tipe Jadwal dinyatakan sebagai tipe privat, sehingga susunan penyimpanannya disembunyikan pada bagian privat.
package Jadwal_Piket is
type Hari is (Senin, Selasa, Rabu, Kamis, Jumat);
type Jadwal is private;
procedure Tetapkan (J : in out Jadwal; H : Hari; Siapa : String);
function Penjaga (J : Jadwal; H : Hari) return String;
function Terisi (J : Jadwal; H : Hari) return Boolean;
private
Lebar_Nama : constant := 24;
type Daftar_Nama is array (Hari) of String (1 .. Lebar_Nama);
type Daftar_Status is array (Hari) of Boolean;
type Jadwal is record
Nama : Daftar_Nama := (others => (others => ' '));
Status : Daftar_Status := (others => False);
end record;
end Jadwal_Piket;Bagian privat memuat record Jadwal dengan dua komponen array, yaitu Nama dan Status, beserta konstanta Lebar_Nama yang menentukan panjang penyimpanan. Konstanta itu tidak diekspor, sehingga keputusan penyimpanan hanya diketahui oleh package induk dan unit anaknya. Operasi yang diekspor cukup untuk kebutuhan umum klien, yaitu menetapkan penjaga, membacanya, dan memeriksa apakah sebuah hari sudah terisi. Persoalan format laporan sengaja tidak ditempatkan di sini karena perluasan seperti itu lebih tepat sebagai unit anak.
Badan package induk mengimplementasikan ketiga operasi yang dijanjikan spesifikasinya. Prosedur Tetapkan menolak nama yang melebihi Lebar_Nama dengan memunculkan Constraint_Error, lalu menyimpan nama dengan spasi pengisi hingga lebar penuh. Dua fungsi pembaca hanya membaca isi array dan status hari, sehingga badan tetap ringkas.
package body Jadwal_Piket is
procedure Tetapkan (J : in out Jadwal; H : Hari; Siapa : String) is
begin
if Siapa'Length > Lebar_Nama then
raise Constraint_Error;
end if;
J.Nama (H) := (others => ' ');
J.Nama (H) (1 .. Siapa'Length) := Siapa;
J.Status (H) := True;
end Tetapkan;
function Penjaga (J : Jadwal; H : Hari) return String is
begin
if not J.Status (H) then
return "";
end if;
return J.Nama (H);
end Penjaga;
function Terisi (J : Jadwal; H : Hari) return Boolean is
begin
return J.Status (H);
end Terisi;
end Jadwal_Piket;Penempatan perluasan pada unit anak menjaga spesifikasi induk tetap kecil dan stabil. Pada program yang besar, satu package tunggal memaksa semua klien membaca antarmuka yang luas, dan setiap penambahan fitur mengompilasi ulang seluruh klien. Hierarki unit memecah persoalan itu, yaitu kebutuhan umum berada pada induk, sedangkan kebutuhan kelompok klien tertentu berada pada unit anaknya. Susunan seperti ini juga memudahkan pembagian kerja antartim, karena setiap tim bertanggung jawab atas unit anak miliknya.
Unit anak yang memperluas induk#
Unit anak dideklarasikan dengan nama yang diawali nama induk dan tanda titik, misalnya Jadwal_Piket.Laporan. Spesifikasinya hanya menambahkan satu prosedur cetak, sedangkan badannya menampilkan laporan untuk setiap hari.
package Jadwal_Piket.Laporan is
procedure Cetak (J : Jadwal);
end Jadwal_Piket.Laporan;with Ada.Text_IO;
with Ada.Strings;
with Ada.Strings.Fixed;
package body Jadwal_Piket.Laporan is
procedure Cetak (J : Jadwal) is
begin
for H in Hari loop
Ada.Text_IO.Put (Hari'Image (H) & " : ");
if J.Status (H) then
Ada.Text_IO.Put_Line
(Ada.Strings.Fixed.Trim (J.Nama (H), Ada.Strings.Both));
else
Ada.Text_IO.Put_Line ("(kosong)");
end if;
end loop;
end Cetak;
end Jadwal_Piket.Laporan;Badan unit anak menggunakan Hari, J.Status, dan J.Nama secara langsung, tanpa mengulang deklarasi apa pun. Hak akses ini khas unit anak, yaitu bagian privat induk terlihat di dalam unit anak, baik pada spesifikasi maupun badannya. Unit di luar hierarki tidak memperoleh hak tersebut, sebagaimana dibuktikan pada bagian Kesalahan umum. Selain itu, nama berkas unit anak mengikuti nama unitnya dengan titik diganti tanda hubung.
Unit anak dapat bersarang lebih dalam lagi, misalnya anak dari anak, dan setiap tingkat memperoleh hak yang sama atas bagian privat seluruh leluhurnya. Sebaliknya, unit anak tidak saling melihat bagian privat satu sama lain, sehingga dua unit anak dari Jadwal_Piket tetap terisolasi. Hak istimewa mengalir ke bawah sepanjang garis keturunan, bukan ke samping antarsaudara. Aturan ini membuat hierarki yang dalam tetap dapat dipahami, karena setiap unit hanya bergantung pada garis induknya.
Klien yang menggunakan unit anak#
Program utama memuat dua klausa with, yaitu with Jadwal_Piket; untuk tipe dan operasi induk, serta with Jadwal_Piket.Laporan; untuk prosedur cetak. Menyebut unit anak pada klausa with tidak membuat nama induk ikut terlihat, sehingga kedua klausa itu sama-sama diperlukan.
with Jadwal_Piket;
with Jadwal_Piket.Laporan;
procedure Uji_Jadwal is
J : Jadwal_Piket.Jadwal;
begin
Jadwal_Piket.Tetapkan (J, Jadwal_Piket.Senin, "Budi");
Jadwal_Piket.Tetapkan (J, Jadwal_Piket.Selasa, "Citra");
Jadwal_Piket.Tetapkan (J, Jadwal_Piket.Kamis, "Dewi");
Jadwal_Piket.Laporan.Cetak (J);
end Uji_Jadwal;# kompilasi seluruh program dari direktori yang memuat berkas-berkasnya
gnatmake uji_jadwal.adbgcc -c uji_jadwal.adb
gcc -c jadwal_piket.adb
gcc -c jadwal_piket-laporan.adb
gnatbind -x uji_jadwal.ali
gnatlink uji_jadwal.ali# jalankan program hasil kompilasi
./uji_jadwalSENIN : Budi
SELASA : Citra
RABU : (kosong)
KAMIS : Dewi
JUMAT : (kosong)Keluaran menampilkan lima hari, dengan dua hari yang dibiarkan kosong. gnatmake menemukan badan package induk dan badan unit anak secara otomatis, kemudian menautkannya menjadi satu program bersama program utama. Nama hari dicetak dengan huruf kapital karena atribut 'Image pada tipe enumerasi menghasilkan bentuk tersebut.
Penambahan unit anak juga berdampak ringan pada kompilasi. Karena spesifikasi induk tidak berubah, unit anak baru cukup dikompilasi bersama klien yang menggunakannya, sementara klien lama yang tidak menyebut unit anak tersebut tidak tersentuh. Perilaku ini menegaskan peran unit anak sebagai perluasan yang aman terhadap kode yang sudah ada.
Kesalahan umum#
Dua kesalahan berikut menguji aturan pokok unit anak, yaitu penamaan berkasnya dan batas hak akses terhadap bagian privat induk. Keduanya terdeteksi oleh perkakas kompilasi, sehingga perbaikannya selalu jelas.
Kesalahan pertama adalah menamai berkas unit anak dengan titik, mengikuti bentuk nama unitnya. GNAT tidak akan menemukan berkas tersebut ketika menelusuri dependensi program.
# ganti nama berkas unit anak ke bentuk yang salah
mv jadwal_piket-laporan.ads jadwal_piket.laporan.ads
# paksa kompilasi ulang program utama
touch uji_jadwal.adb
# kompilasi gagal karena berkas unit anak tidak ditemukan
gnatmake uji_jadwal.adbgcc -c uji_jadwal.adb
uji_jadwal.adb:2:06: error: "Uji_Jadwal (body)" depends on "Jadwal_Piket.Laporan (body)"
uji_jadwal.adb:2:06: error: "Jadwal_Piket.Laporan (body)" depends on "Jadwal_Piket.Laporan (spec)"
uji_jadwal.adb:2:06: error: file "jadwal_piket-laporan.ads" not found
gnatmake: "uji_jadwal.adb" compilation errorPesan error menelusuri rantai dependensi dari badan program utama sampai spesifikasi unit anak, lalu menyatakan berkas yang tidak ditemukan. Perbaikannya adalah mengembalikan nama berkas ke bentuk dengan tanda hubung, dan kompilasi berikutnya berhasil.
# kembalikan nama berkas yang benar
mv jadwal_piket.laporan.ads jadwal_piket-laporan.ads
# kompilasi ulang hingga berhasil
gnatmake uji_jadwal.adbgcc -c uji_jadwal.adb
gnatbind -x uji_jadwal.ali
gnatlink uji_jadwal.aliKesalahan kedua adalah mengakses bagian privat induk dari unit di luar hierarki. Hak akses itu berlaku hanya untuk unit anak, sehingga program utama yang berada di luar hierarki ikut ditolak.
with Jadwal_Piket;
-- Berkas ini sengaja salah: unit luar mengakses bagian privat induk.
procedure Uji_Privat_Luar is
J : Jadwal_Piket.Jadwal;
begin
J.Status (Jadwal_Piket.Senin) := True;
end Uji_Privat_Luar;# kompilasi unit luar yang menyentuh bagian privat induk
gnatmake uji_privat_luar.adbgcc -c uji_privat_luar.adb
uji_privat_luar.adb:7:04: error: invalid prefix in selected component "J"
gnatmake: "uji_privat_luar.adb" compilation errorPesan error menyatakan awalan seleksi tidak sah karena J.Status tidak terlihat dari luar hierarki. Perbaikannya adalah menggunakan operasi yang diekspor, misalnya Tetapkan. Apabila operasi semacam itu memang perlu dibagikan, deklarasikan pada package induk atau pada unit anak yang tepat, bukan dengan mengakses bagian privat secara langsung.
Ringkasan#
- Unit anak dideklarasikan dengan nama bertitik seperti
Jadwal_Piket.Laporan, dan GNAT menyimpannya pada berkas dengan tanda hubung sepertijadwal_piket-laporan.ads. - Unit anak melihat bagian privat induknya secara langsung, sehingga perluasan dapat memanfaatkan representasi internal tanpa mengubah spesifikasi induk.
- Klien yang menggunakan unit anak wajib menuliskan klausa with untuk induk dan anaknya, karena menyebut anak saja tidak menjadikan induk terlihat.
Referensi#
- Ada Reference Manual 2022, Bagian 10.1.1 Compilation Units - Library Units — Ada Resource Association — Bagian ini menetapkan hubungan induk dan anak antarunit pustaka beserta aturan kompilasinya.
- Introduction to Ada — AdaCore — Bab Modular programming membahas child packages, termasuk penamaan berkas dan visibilitasnya.