Domain modeling mengajari kita menata kode sesuai masalah bisnis; design pattern mengajari kita menata kode sesuai masalah desain yang muncul berulang. Hampir semua program bertemu masalah sejenis: penciptaan objek yang rumit, antarmuka yang tidak kompatibel, algoritma yang perlu ditukar saat program berjalan. Alih-alih menemukan jalan keluar dari nol setiap kali, komunitas perangkat lunak menabung solusi terbaik, memberi solusi tersebut nama, dan menyusun katalognya. Chapter ini memperkenalkan katalog klasik tersebut, tiga kategorinya, dan satu hal yang sering luput: kita sudah memakai pola tersebut setiap hari lewat pustaka standar Java.

Asal Mula Katalog GoF #

Titik awalnya adalah buku Design Patterns: Elements of Reusable Object-Oriented Software yang diterbitkan tahun 1994 oleh empat penulis — Erich Gamma, Richard Helm, Ralph Johnson, dan John Vlissides — yang kemudian dikenal sebagai Gang of Four (GoF). Buku tersebut memuat 23 pola yang diambil dari desain sistem nyata, masing-masing didokumentasikan dengan format yang seragam: nama pola, masalah yang dijawab, struktur solusi, dan konsekuensi yang menyertainya. Format ini penting karena pola bukan potongan kode siap salin, melainkan pengetahuan berbentuk keputusan desain.

Sebelum GoF, gagasan pattern language dipinjam dari dunia arsitektur bangunan melalui karya Christopher Alexander. Para penulis GoF menerjemahkan ide itu ke perangkat lunak: masalah desain yang berulang layak memiliki jawaban baku yang bisa dirujuk namanya. Sejak 1994, katalog terus bertambah — Repository, Unit of Work, dan Dependency Injection adalah pola yang lahir sesudahnya — sehingga angka 23 hanyalah titik sejarah, bukan batas.

Tiga Kategori Pola #

GoF membagi 23 polanya ke dalam tiga kategori sesuai sifat masalah yang dijawab:

KategoriMasalah intiContoh polaWujud di JDK
CreationalBagaimana objek diciptakanFactory Method, Abstract Factory, Builder, Prototype, SingletonList.of(...) sebagai static factory
StructuralBagaimana objek disusun dan dijembataniAdapter, Decorator, Facade, Composite, ProxyBufferedReader sebagai decorator
BehavioralBagaimana objek berinteraksi dan membagi tanggung jawabStrategy, Observer, Command, Iterator, Template MethodComparator sebagai strategy, Iterator pada koleksi

Pembagian ini membantu kita mencari pola yang tepat: bila persoalannya “kode klien terlalu tahu cara membuat objek”, jawabannya ada di keluarga creational; bila “antarmuka tidak cocok”, kita melirik structural; bila “perilaku perlu berganti saat runtime”, kandidatnya behavioral.

Pola sebagai Kosakata Komunikasi #

Manfaat pola yang paling sering diremehkan bukan kode, melainkan nama. Kalimat “bungkus NotifikasiEmail dengan decorator supaya bisa ditumpuk lapisan sms dan log” memindahkan seluruh struktur desain ke kepala pendengar dalam satu kalimat — tanpa pola, kalimat yang sama perlu mendeskripsikan class pembungkus, pewarisan antarmuka yang sama, dan penerusan method satu per satu. Nama pola bekerja seperti istilah medis bagi dokter: padat, presisi, dan langsung memicu struktur yang sudah dikenal.

Nama juga mengawasi arah diskusi desain. Saat seorang reviewer menulis “kelas ini sebaiknya jadi observer, bukan di-polling tiap detik”, usulan tersebut bisa dibantah atau didukung dengan argumen konsekuensi pola — decoupling yang dihasilkan, urutan notifikasi yang harus dijaga — bukan dengan selera pribadi. Dalam tim, kosakata bersama seperti ini memperpendek rapat dan mengurangi salah paham.

Pola yang Sudah Anda Pakai Setiap Hari #

Pustaka standar Java adalah museum pola yang hidup: kita memakai strategy setiap kali mengurutkan, decorator setiap kali membungkus pembaca berkas, dan static factory setiap kali memanggil List.of. Program berikut membuktikan ketiganya dalam satu berkas singkat:

java PolaDiJdk.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class PolaDiJdk {
    public static void main(String[] args) throws IOException {
        List<String> namaProduk = new ArrayList<>(List.of("Tas", "Buku", "Pena", "Penghapus"));

        // Strategy (behavioral): algoritma pengurutan dikemas sebagai objek yang bisa dioper
        Comparator<String> panjangLaluAbjad = Comparator.comparingInt(String::length)
                .thenComparing(Comparator.naturalOrder());
        namaProduk.sort(panjangLaluAbjad);
        System.out.println("urut panjang lalu abjad: " + namaProduk);

        // Decorator (structural): pembungkus menambah perilaku di atas pembaca lain
        try (BufferedReader pembaca = new BufferedReader(
                new StringReader("baris pertama\nbaris kedua"))) {
            System.out.println("dibaca: " + pembaca.readLine());
            System.out.println("dibaca: " + pembaca.readLine());
        }
    }
}

Output yang Dihasilkan:

text
urut panjang lalu abjad: [Tas, Buku, Pena, Penghapus]
dibaca: baris pertama
dibaca: baris kedua

Mari kita bedah tiga pola yang bersarang di program tersebut:

  1. Baris 12 (new ArrayList<>(List.of(...))): List.of adalah static factory milik JDK — satu nama penciptaan yang jelas untuk koleksi yang tidak dapat diubah. Daftar tersebut dibungkus ArrayList karena pengurutan di tempat (sort) menuntut daftar yang bisa diubah.
  2. Baris 14-16 (Comparator.comparingInt(...).thenComparing(...)): Comparator adalah strategy — algoritma pembanding dikemas sebagai objek yang bisa dioper ke sort. Method thenComparing menyusun strategi kecil menjadi strategi gabungan tanpa membuat class baru.
  3. Baris 17 (namaProduk.sort(...)): daftar menerima algoritma dari luar; struktur data tetap pasif. Inilah inti strategy: kode yang berubah (aturan urut) dipisahkan dari kode yang tidak (mekanisme urut).
  4. Baris 20-24 (new BufferedReader(new StringReader(...))): BufferedReader adalah decorator yang menambah perilaku buffering di atas pembaca apa pun — di sini StringReader. try-with-resources menutup kedua lapisan sekaligus, dan pemanggil tidak perlu tahu lapisan dalamnya StringReader atau FileReader.

Lima chapter berikutnya membedah pola-pola tersering satu per satu dengan program yang dapat dijalankan, dimulai dari pola penciptaan yang paling sering dipakai: factory pattern.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

PolaDiJdk.java

Unduh berkas
java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class PolaDiJdk {
    public static void main(String[] args) throws IOException {
        List<String> namaProduk = new ArrayList<>(List.of("Tas", "Buku", "Pena", "Penghapus"));

        // Strategy (behavioral): algoritma pengurutan dikemas sebagai objek yang bisa dioper
        Comparator<String> panjangLaluAbjad = Comparator.comparingInt(String::length)
                .thenComparing(Comparator.naturalOrder());
        namaProduk.sort(panjangLaluAbjad);
        System.out.println("urut panjang lalu abjad: " + namaProduk);

        // Decorator (structural): pembungkus menambah perilaku di atas pembaca lain
        try (BufferedReader pembaca = new BufferedReader(
                new StringReader("baris pertama\nbaris kedua"))) {
            System.out.println("dibaca: " + pembaca.readLine());
            System.out.println("dibaca: " + pembaca.readLine());
        }
    }
}