Program yang tumbuh sebagai satu skrip raksasa mulai nyaman, lalu berubah menjadi beban: satu perubahan kecil di bagian perhitungan bisa merusak bagian laporan, dan tidak ada potongan yang bisa diuji terpisah. Modularisasi memecah monolit menjadi fungsi-fungsi kecil dengan tanggung jawab tunggal. Chapter ini membahas:

  • Pecahan monolit 60 baris menjadi tiga modul: baca data, hitung, format.
  • Antarmuka eksplisit (input/output terdefinisi) dan arah ketergantungan.
  • Konsep coupling vs cohesion, fungsi murni vs efek samping, dan aturan tiga duplikasi.

Masalah Monolit #

Skrip monolit khas menggabungkan empat pekerjaan sekaligus: membaca data, memvalidasi, menghitung statistik, dan memformat laporan. Bentuknya (dipadatkan dari skrip 60 baris yang asli):

matlab
% monolitSkor.m - SEBELUM: baca + hitung + format menyatu
nama = {'Budi','Siti','Ani','Dewi','Eka','Fajar','Gita','Hadi'};
skor = [78 92 65 88 73 95 60 81];

% ... validasi input dan pembersihan data, 15 baris ...
% ... statistik antara dan tampilan debug, 20 baris ...

m = mean(skor);  md = median(skor);
mn = min(skor);  mx = max(skor);
idx = find(skor == mx, 1);
fprintf('Laporan Skor Ujian (%d siswa)\n', numel(nama));
fprintf('Rata-rata : %.1f\n', m);
% ... 8 baris fprintf laporan lainnya ...

Mekanisme masalahnya: semua variabel berada di base workspace yang sama, sehingga kegagalan di satu bagian menular ke semua bagian. Kita tidak bisa menguji perhitungan statistik tanpa menjalankan pembacaan data, dan tidak bisa mengganti format laporan tanpa menyentuh logika hitung. Batas antar tanggung jawab tidak pernah eksplisit.

Satu Modul Satu Tanggung Jawab #

Refactor memecah skrip tersebut menjadi tiga fungsi, satu berkas per fungsi: bacaSkor.m (sumber data), ringkasStatistik.m (perhitungan), buatLaporan.m (format). Alur end-to-end-nya menjadi tiga baris yang terbaca seperti kalimat:

matlab
    s = bacaSkor();                 % modul 1: sumber data
    st = ringkasStatistik(s.skor);  % modul 2: hitung statistik
    lap = buatLaporan(s, st);       % modul 3: format laporan

Mari kita bedah baris-baris kode di atas:

  1. Baris 1 (s = bacaSkor();): modul data mengembalikan struct s berisi nama dan skor. Sumber data bisa berganti (CSV, basis data) tanpa menyentuh dua modul lainnya selama bentuk keluarannya tetap.
  2. Baris 2 (st = ringkasStatistik(s.skor);): modul hitung menerima vektor skor apa pun — fungsi ini tidak mengenal bacaSkor, tidak membaca berkas, dan tidak mencetak apa pun. Murni masuk-keluar data.
  3. Baris 3 (lap = buatLaporan(s, st);): modul format menerima hasil dua modul sebelumnya dan mengembalikan cell baris teks. Fungsi ini boleh diganti dengan versi HTML atau Excel tanpa mengubah hasil hitung.

Diagram berikut memperlihatkan aliran data antar modul — panah dari keluaran satu modul ke masukan modul berikutnya:

contoh.m di bawah halaman ini menulis ketiga modul sebagai berkas .m sungguhan (fungsi dengan help text), menempatkannya di folder sementara, lalu menjalankan alur end-to-end dan membandingkan hasilnya dengan versi monolit. Perbandingan isequal membuktikan refactor behavior-preserving — hasil tidak berubah, hanya strukturnya.

Output yang Dihasilkan (dari contoh.m lengkap):

text
== Bagian 1: menulis 3 modul fungsi ==
bacaSkor.m, ringkasStatistik.m, buatLaporan.m ditulis
== Bagian 2: uji alur end-to-end ==
Laporan Skor Ujian (8 siswa)
Rata-rata : 79.0
Median    : 79.5
Rentang   : 60 - 95
Tertinggi : Fajar (95)
== Bagian 3: verifikasi refactor ==
hasil identik dengan monolit: 1
jumlah modul: 3 (dari 1 fungsi monolit)
== Bagian 4: pembersihan ==
modul dihapus: exist = 0

Antarmuka Eksplisit dan Arah Ketergantungan #

Antarmuka eksplisit berarti kontrak tiap modul tertulis jelas: apa inputnya, apa outputnya, dan apa efeknya. Bantuan terbaik datang dari help text — R = RINGKASSTATISTIK(SKOR) mengembalikan struct field mean, median, min, dan max adalah kontrak yang bisa dipegang pemanggil. Arah ketergantungan dijaga tetap satu arah: modul tingkat rendah (hitung, format) tidak mengenal modul tingkat tinggi (pipeline utama), tetapi tidak sebaliknya. Dua ukuran klasik memandu desain ini:

UkuranPertanyaanTarget
Cohesionseberapa fokus tanggung jawab satu modul?tinggi
Couplingseberapa banyak modul saling mengenal?rendah

Fungsi Murni vs Efek Samping #

AspekFungsi murniFungsi berekfek samping
Keluaran ditentukan olehinput sajainput + state luar
ContohringkasStatistik(skor)fprintf, save, plot, saveas
Dijalankan dua kalihasil selalu samahasil bisa berbeda (berkas tertimpa, figur baru)
Penempatan idealinti analisis/perhitungantepi program (I/O, tampilan)

Strateginya: kumpulkan efek samping di tepi pipeline. bacaSkor (I/O) masuk di depan, buatLaporan memformat tanpa mencetak (murni), dan pencetakan ke Command Window atau penyimpanan berkas terjadi di skrip utama. Inti perhitungan tetap murni sehingga mudah diuji ulang — pola yang sama dipakai chapter Reproducible Projects.

Aturan Tiga Duplikasi #

Duplikasi kode adalah monolit yang menyamar. Aturan praktisnya: boleh menulis potongan sama untuk kedua kalinya (duplikasi kedua sering masih samar), tetapi pada kemunculan ketiga wajib mengekstrak fungsi bersama. Tiga salinan berarti satu perbaikan galat harus diterapkan tiga kali — dan satu yang terlupa menjadi galat laten yang paling sulit ditemukan.

Ringkasan #

Modularisasi memecah monolit menjadi fungsi bertanggung jawab tunggal dengan antarmuka eksplisit: bacaSkor menyediakan data, ringkasStatistik menghitung, buatLaporan memformat. Arah ketergantungan dijaga minim, cohesion tinggi, coupling rendah, fungsi inti dibuat murni, dan duplikasi ketiga memicu ekstraksi. contoh.m membuktikan hasil refactor identik dengan monolit (isequal = 1) lalu membersihkan seluruh berkas uji.