Pada Chapter First-Class Functions kita sudah melihat bahwa function bisa disimpan di variabel, dikirim sebagai argument, dan dikembalikan dari function lain. Melihat fakta itu sebagai trik saja akan sayang — kemampuan tersebut adalah pintu masuk ke pola desain yang membuat kode mudah dikembangkan tanpa dirombak. Pada chapter ini kita memanfaatkan pola itu secara sadar lewat dua teknik utama: strategy pattern dan dispatch dictionary.

Definisi dan Posisinya #

Higher-order function adalah function yang melakukan salah satu dari dua hal berikut:

  • menerima satu atau lebih function sebagai argument, atau
  • mengembalikan function sebagai nilai balik.

Contoh pertama sudah sering kita pakai tanpa menyadarinya: sorted(data, key=...) menerima function key, dan method list.sort(key=...) bekerja dengan cara yang sama. Contoh kedua kita jumpai pada function pembuat seperti buat_pangkat(2) yang mengembalikan function siap pakai.

Pola Strategy: Algoritma Dikirim dari Luar #

Masalah umum: satu proses inti, banyak variasi cara. Misalkan program kasir harus menampilkan harga dalam dua format — rupiah lengkap dan ringkas. Daripada menanam pilihan format di dalam badan function, kita kumpulkan setiap format sebagai function mandiri lalu memilihnya dari dictionary:

python
def format_rupiah(nominal):
    return f"Rp{nominal:,}".replace(",", ".")

def format_ringkas(nominal):
    return f"{nominal // 1000}rb"

format_strategi = {
    "lengkap": format_rupiah,
    "ringkas": format_ringkas,
}

def tampilkan_harga(nominal, gaya):
    print(format_strategi[gaya](nominal))

tampilkan_harga(1500000, "lengkap")
tampilkan_harga(1500000, "ringkas")

Output yang Dihasilkan:

text
Rp1.500.000
1500rb

Mari kita telaah apa yang terjadi pada baris-baris kode di atas:

  1. Baris 1-5 (def format_rupiah dan def format_ringkas): setiap algoritma format berdiri sendiri sebagai function kecil dan bisa diuji terpisah.
  2. Baris 7-10 (format_strategi = {...}): dictionary menyimpan object function sebagai value — bukan hasil pemanggilannya, karena tidak ada tanda kurung setelah nama function.
  3. Baris 12-13 (def tampilkan_harga): proses inti hanya melakukan lookup format_strategi[gaya] lalu memanggilnya. Function ini tidak perlu tahu detail format apapun.
  4. Baris 15-16: pemanggil memilih strategi lewat string; menambah gaya baru cukup menambah satu entry dictionary tanpa menyentuh badan tampilkan_harga.

Dispatch Dictionary vs Rantai if-elif #

Bentuk klasik pemetaan kode ke aksi adalah rantai if-elif. Bandingkan kedua implementasi status pesanan berikut:

python
def status_if(kode):
    if kode == "P":
        return "Pesanan diproses"
    elif kode == "K":
        return "Pesanan dikirim"
    elif kode == "S":
        return "Pesanan selesai"
    else:
        return "Kode tidak dikenal"

STATUS = {
    "P": "Pesanan diproses",
    "K": "Pesanan dikirim",
    "S": "Pesanan selesai",
}

def status_dict(kode):
    return STATUS.get(kode, "Kode tidak dikenal")

for kode in ["P", "K", "S", "X"]:
    print(f"{kode}: if -> {status_if(kode)} | dict -> {status_dict(kode)}")

Output yang Dihasilkan:

text
P: if -> Pesanan diproses | dict -> Pesanan diproses
K: if -> Pesanan dikirim | dict -> Pesanan dikirim
S: if -> Pesanan selesai | dict -> Pesanan selesai
X: if -> Kode tidak dikenal | dict -> Kode tidak dikenal

Kedua implementasi menghasilkan output identik untuk semua kode, termasuk kode tak dikenal X. Perbedaannya baru terasa saat program berkembang:

AspekRantai if-elifDispatch dictionary
Menambah kasus baruEdit badan function — setiap edit berisiko merusak cabang lainTambah satu entry dictionary
Panjang functionTumbuh linear mengikuti jumlah kasusTetap satu baris
Cara lookupDievaluasi berurutan dari cabang pertamaSatu kali lookup hash
Cocok untukLogika bercabang dengan kondisi beragam (rentang, gabungan)Pemetaan langsung key ke nilai atau function

Aturan praktisnya: ketika setiap cabang hanya berupa pemetaan kode → aksi, dispatch dictionary menang tipis dan semakin jauh semakin menang. Ketika cabang memuat kondisi kompleks seperti rentang usia atau kombinasi flag, if-elif tetap wajar dipakai.

Function Factory: Validator Sesuai Pesanan #

Higher-order function kedua jenisnya — yang mengembalikan function — sangat cocok memproduksi validator yang aturannya diatur saat pabrik dipanggil:

python
def buat_validasi_panjang(batas_min, pesan):
    def validasi(teks):
        if len(teks) < batas_min:
            return f"Tidak valid: {pesan}"
        return "Valid"
    return validasi

validasi_email = buat_validasi_panjang(3, "email terlalu pendek")
validasi_password = buat_validasi_panjang(8, "password minimal 8 karakter")

print(validasi_email("[email protected]"))
print(validasi_password("kopi"))

Output yang Dihasilkan:

text
Valid
Tidak valid: password minimal 8 karakter

Satu cetakan buat_validasi_panjang menghasilkan dua validator dengan ambang dan pesan berbeda. Inner function validasi membaca batas_min dan pesan dari lingkungan function pembungkusnya — mekanisme pengambilan variabel lintingkungan ini bernama closure dan menjadi fokus Chapter Closures.