Arsitektur

Merancang struktur modul, struktur data utama, dan antarmuka program sebelum kode proyek ditulis.

Daftar isi

Topik ini merancang arsitektur proyek rekap nilai ujian sebelum kode implementasinya ditulis. Pembahasannya mencakup pembagian tanggung jawab antara modul dan skrip, struktur data utama, kontrak data setiap subrutin, serta antarmuka command line yang dijanjikan kepada pengguna. Setelah topik ini diikuti, setiap berkas proyek dapat ditulis secara terpisah karena tugas, bentuk data, dan antarmukanya sudah disepakati lebih dahulu.

Pembagian tanggung jawab modul dan skrip#

Arsitektur proyek ini membagi kode menjadi dua berkas dengan tugas yang tidak tercampur. Berkas RekapNilai.pm memuat seluruh logika, yaitu subrutin baca_data untuk membaca dan memvalidasi berkas data, hitung_rata untuk menghitung rata-rata sebuah array nilai, dan ringkas untuk menyusun statistik per siswa beserta statistik kelas. Berkas rekap.pl menjadi antarmuka command line, yaitu menerima argumen, memuat modul, memanggil subrutinnya, dan mencetak laporan. Pembagian ini mengikuti konvensi yang dijelaskan dokumentasi Perl, yaitu modul berisi kode yang dapat digunakan ulang, sedangkan skrip merupakan program yang memanggilnya.

Alasan utama pembagian ini adalah pengujian. Subrutin pada modul dapat dipanggil langsung dari berkas pengujian tanpa argumen command line dan tanpa memulai proses baru, sehingga perilaku setiap subrutin dapat dibandingkan dengan nilai harapan. Bila logika bercampur dengan pencetakan di dalam satu skrip, satu-satunya cara mengujinya adalah menjalankan skrip utuh lalu membaca keluarannya, dan cara itu rapuh serta sulit diotomatisasi. Modul yang murni mengolah dan mengembalikan data mudah diuji, sedangkan skrip yang dibuat tipis jarang memuat perilaku yang perlu diuji.

Struktur data utama#

Struktur data utama proyek ini adalah hash dari array, yaitu hash yang kuncinya nama siswa dan nilai tiap kuncinya berupa array berisi seluruh nilai milik siswa tersebut. Bentuk ini cocok dengan cara data dibaca, karena setiap baris berkas menyumbang satu nilai untuk satu kunci dan penambahan elemen array tidak memerlukan pencarian posisi. Struktur berikut memperlihatkan isinya untuk berkas contoh pada topik pemilihan proyek.

perl Contoh: struktur data rekap
use strict;
use warnings;
use Data::Dumper;

# hash dari array: kunci berupa nama siswa, isinya array nilai
my %rekap = (
    Ali   => [ 80, 90 ],
    Budi  => [ 75, 65 ],
    Citra => [88],
);

$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Indent   = 1;
print Dumper( \%rekap );
text Keluaran
$VAR1 = {
  'Ali' => [
    80,
    90
  ],
  'Budi' => [
    75,
    65
  ],
  'Citra' => [
    88
  ]
};

Hash dipilih untuk menggantikan array dari hash yang menyimpan nama pada setiap elemennya. Pencarian nilai satu siswa pada hash langsung menuju kuncinya, sedangkan pada array pencarian tersebut harus menelusuri elemen satu per satu. Kebutuhan lain proyek ini hanya agregasi, sehingga struktur terdangkal seperti ini sudah cukup dan tidak perlu dibuat lebih dalam. Urutan kunci sendiri tidak menjadi bagian dari struktur, sebab urutan itu diatur ketika laporan dicetak, bukan ketika data disimpan.

Kontrak data antarsubrutin#

Kontrak subrutin menyatakan argumen yang diterima dan nilai balik yang dikembalikan. Kontrak ini ditetapkan sebelum kode ditulis karena seluruh pengujian pada implementasi nanti membandingkan nilai balik dengan ketentuan di sini. Untuk modul RekapNilai, kontraknya dinyatakan sebagai tabel berikut.

SubrutinArgumenNilai balik
baca_dataJalur berkas dataReferensi hash nilai per siswa, lalu jumlah baris tidak valid
hitung_rataReferensi array nilaiRata-rata berupa satu angka, atau 0 untuk array kosong
ringkasReferensi hash nilai per siswaReferensi hash berisi daftar siswa dan statistik kelas

Bentuk nilai balik ringkas juga perlu disepakati sampai ke isinya, karena struktur itulah yang dibaca skrip saat menyusun laporan. Pada implementasinya, setiap argumen tiba di dalam subrutin melalui array @_, sehingga baris pertama setiap subrutin menyalin isinya ke variabel dengan nama yang jelas. Pengiriman referensi pada tabel di atas juga disengaja, karena array dan hash yang dikirim langsung diratakan menjadi satu daftar pada @_, sedangkan referensi tetap berupa satu nilai yang utuh. Contoh berikut membangun struktur tersebut dan menelusurinya seperti yang akan dilakukan skrip.

perl Contoh: struktur hasil ringkas
use strict;
use warnings;

# struktur yang dikembalikan ringkas: satu hash berisi dua kelompok informasi
my $ringkasan = {
    siswa => [
        { nama => 'Ali',   banyak => 2, rata => 85 },
        { nama => 'Budi',  banyak => 2, rata => 70 },
    ],
    tertinggi    => 90,
    terendah     => 65,
    rata_kelas   => 77.5,
    banyak_nilai => 4,
};

foreach my $s ( @{ $ringkasan->{siswa} } ) {
    printf "%s: rata-rata %.2f dari %d nilai\n",
        $s->{nama}, $s->{rata}, $s->{banyak};
}

printf "Kelas: tertinggi %d, terendah %d, rata-rata %.2f\n",
    $ringkasan->{tertinggi}, $ringkasan->{terendah}, $ringkasan->{rata_kelas};
text Keluaran
Ali: rata-rata 85.00 dari 2 nilai
Budi: rata-rata 70.00 dari 2 nilai
Kelas: tertinggi 90, terendah 65, rata-rata 77.50

Antarmuka command line#

Antarmuka command line adalah kontrak proyek dengan penggunanya, sehingga dirancang sebelum kode skrip ditulis. Proyek ini sengaja menggunakan bentuk paling sederhana, yaitu satu argumen posisi berupa jalur berkas data tanpa opsi tambahan. Menjalankan perl rekap.pl tanpa argumen membaca berkas bawaan nilaipercobaan.txt dari direktori kerja, sedangkan menjalankan perl rekap.pl nilaibermasalah.txt membaca berkas yang disebut. Bentuk ini cukup karena proyek hanya menerima satu masukan, dan penambahan opsi semacam --format baru dipertimbangkan bila kebutuhannya benar-benar muncul.

Perilaku program pada tiap keadaan juga menjadi bagian dari rancangan, termasuk kode keluar yang diserahkan kepada pemanggil. Kode keluar memungkinkan perintah lain, misalnya skrip pengotomatisasi, menilai keberhasilan program tanpa membaca isinya.

KeadaanPerilaku programKode keluar
Tanpa argumenMembaca nilaipercobaan.txt dari direktori kerja0 pada data yang sah
Argumen berisi jalur berkasMembaca berkas yang disebut0 pada data yang sah
Ada baris tidak validPesan per baris ke STDERR, laporan tetap dicetak0
Berkas tidak adaPesan error ke STDERR, lalu program berhenti2
Tidak ada nilai yang sahPesan error ke STDERR, lalu program berhenti2

Angka 2 pada dua keadaan terakhir berasal dari nilai $! saat open gagal, yang dibawa die sebagai kode keluarnya. Kebijakan lengkapnya diwujudkan bersama kode implementasinya.

Diagram alur data#

Diagram berikut merangkum arsitektur yang sudah dirancang, yaitu alur dari berkas data menuju laporan melalui modul dan skrip.

Alur data proyek rekap nilai ujianberkas databaris nama;nilaidibacaRekapNilai.pmbaca_datahitung_rataringkasdiimporrekap.plcommand linedicetaklaporan tekske layar
Alur data pada proyek rekap nilai ujian

Alurnya bergerak satu arah. Skrip mengimpor modul, modul membaca berkas data dan mengembalikan struktur data, lalu skrip mencetak laporan. Tidak ada jalur balik dari skrip ke modul maupun dari modul ke berkas data, sehingga setiap bagian dapat diperiksa secara terpisah. Modul diuji dengan berkas contoh tanpa menyentuh skrip, sedangkan skrip diperiksa dengan menjalankannya. Diagram juga menegaskan bahwa berkas data dan laporan berada di luar kedua berkas kode, sehingga perubahan format laporan hanya menyentuh skrip, sedangkan perubahan aturan validasi hanya menyentuh modul. Batas perubahan yang jelas semacam ini adalah manfaat arsitektur yang paling sering dirasakan ketika proyek berkembang.

Alur satu arah juga menjelaskan posisi berkas pengujian yang menyertai implementasi. Berkas uji mengimpor RekapNilai secara langsung, memanggil ketiga subrutinnya dengan data kecil, dan membandingkan nilai baliknya, tanpa menyentuh rekap.pl maupun laporannya. Kegagalan pengujian dengan demikian selalu menunjuk pada logika modul, bukan pada tampilan laporan.

Kesalahan umum#

  1. Logika ditulis di dalam skrip antarmuka sehingga subrutinnya tidak dapat diuji tanpa menjalankan seluruh program. Perbaikannya adalah memindahkan setiap subrutin yang mengolah data ke modul dan menyisakan penerimaan argumen serta pencetakan di skrip.
  2. Struktur data dibuat terlalu dalam, misalnya hash dari hash dari array, padahal kebutuhan proyek hanya agregasi sederhana. Perbaikannya adalah memilih struktur terdangkal yang memenuhi kebutuhan laporan, lalu mengembangkannya hanya bila kebutuhan baru muncul.
  3. Kontrak subrutin tidak dinyatakan secara tertulis, sehingga bentuk nilai baliknya berubah saat implementasi berjalan dan kode lain yang bergantung padanya ikut rusak. Perbaikannya adalah menetapkan argumen dan nilai balik setiap subrutin sebelum kode ditulis.

Ringkasan#

  • Proyek dibagi menjadi modul logika RekapNilai.pm dan skrip antarmuka rekap.pl agar logikanya dapat diuji terpisah dari antarmukanya.
  • Data utama berupa hash dari array dengan kunci nama siswa, dan urutan laporannya diatur saat pencetakan dengan sort keys.
  • Setiap subrutin memiliki kontrak argumen dan nilai balik yang tertulis dan menjadi dasar pengujian.
  • Antarmuka command line menggunakan satu argumen posisi dengan kebijakan perilaku dan kode keluar yang ditetapkan lebih dahulu.

Referensi#