Abstraction adalah keterampilan menyembunyikan detail cara kerja di balik kontrak yang ringkas. Seluruh materi section ini melayani satu tujuan itu: interface menyatakan kemampuan tanpa mengikat implementasi, interface segregation menjaga kontrak tetap kecil, dan programming to interface membalik ketergantungan ke arah kontrak. Bab penutup ini merangkum keputusan desain yang terbentuk dari kaidah tersebut, lalu menerapkannya pada satu alur checkout utuh.
Keputusan Desain yang Terbentuk #
Empat kaidah berikut menjadi panduan saat menyusun desain:
- Susun kontrak dari sisi pemanggil. Daftar member interface ditentukan oleh kebutuhan pemanggil, bukan oleh isi class yang kebetulan sudah ada.
- Jaga kontrak tetap kecil dan fokus. Satu interface menjanjikan satu tanggung jawab agar implementer tidak memikul member yang tidak dipakai.
- Arahkan ketergantungan ke kontrak. Parameter method, field, dan variabel pemanggil bertipe interface sehingga implementasi dapat ditukar.
- Perluas dengan aman. Kemampuan baru masuk lewat interface turunan atau default methods agar class lama tetap stabil.
Studi Kasus: Alur Checkout #
Class Pesanan berikut memakai dua kontrak sekaligus tanpa mengetahui class konkretnya. Perhatikan bahwa Pesanan sama sekali tidak berisi logika pembayaran maupun logika email; class tersebut hanya mengatur urutan langkah dan menyusun isi pesan. Detail pemrosesan menjadi milik kontrak masing-masing, dan pemisahan itu menjaga class tetap kecil.
public class Pesanan
{
public int Total { get; private set; }
public void TambahItem(int harga)
{
Total += harga;
}
public void Selesaikan(IPembayaran metode, IKirimEmail notifikasi)
{
metode.ProsesPembayaran(Total);
notifikasi.Kirim("[email protected]", $"Pesanan selesai. Total dibayar Rp{Total}.");
}
}Alur pemanggilan berikut memasangkan dua pasangan implementasi yang berbeda:
Pesanan pesanan = new Pesanan();
pesanan.TambahItem(80000);
pesanan.TambahItem(70000);
pesanan.Selesaikan(new KartuKredit(), new EmailService());
Pesanan pesananKedua = new Pesanan();
pesananKedua.TambahItem(150000);
pesananKedua.Selesaikan(new Ewallet(), new EmailMarketing());Output yang Dihasilkan:
Kartu kredit: pembayaran Rp150000 diproses (biaya admin Rp2000).
Email terkirim ke [email protected]: Pesanan selesai. Total dibayar Rp150000.
E-Wallet: pembayaran Rp150000 didebit.
Email ke [email protected]: Pesanan selesai. Total dibayar Rp150000.Mari kita bedah baris demi baris:
pesanan.TambahItem(80000)danpesanan.TambahItem(70000)— total pesanan terkumpul sebesar Rp150000.Selesaikan(IPembayaran metode, IKirimEmail notifikasi)— kedua parameter bertipe kontrak; Pesanan tidak mengetahui KartuKredit, Ewallet, maupun EmailService.pesanan.Selesaikan(new KartuKredit(), new EmailService())— pemanggil memasangkan pasangan implementasi pilihan pertama.pesananKedua.Selesaikan(new Ewallet(), new EmailMarketing())— pasangan implementasi berbeda berjalan tanpa mengubah satu baris kode Pesanan sekalipun.
Dua pemanggilan, dua pasangan implementasi, dan class Pesanan tetap sama. Pola inilah tujuan akhir section ini: desain yang bergantung pada kontrak dapat berkembang mengikuti kebutuhan bisnis tanpa mengganggu kode yang sudah stabil.