oopinterfaceMahir4 mnt baca

Interface & Abstract Class

Kontrak kode: interface menjamin kemampuan, abstract class memberi kerangka.

Analogi: Kontrak Kerja dan Cetak Biru

Interface seperti kontrak kerja: "siapa pun yang mengaku sebagai Pembayar wajib punya method bayar()". Ia tidak peduli caranya, yang penting janjinya ditepati. Abstract class seperti cetak biru setengah jadi: sebagian sudah dibangun (method konkrit), sebagian masih sketsa (abstract) yang wajib dilengkapi anak.

Keduanya menjawab masalah yang sama: banyak class berbeda harus bisa diperlakukan sama. Kasir tidak peduli pembayaran via tunai, QRIS, atau transfer; ia hanya butuh objek yang "bisa dibayar".

php
<?php
// Contoh 1: interface sebagai kontrak
interface BisaDibayar {
    public function bayar(float $jumlah): string;
}

class Tunai implements BisaDibayar {
    public function bayar(float $jumlah): string {
        return "Terima tunai Rp" . number_format($jumlah);
    }
}

class Qris implements BisaDibayar {
    public function bayar(float $jumlah): string {
        return "QRIS sebesar Rp" . number_format($jumlah) . " dipindai.";
    }
}

function prosesKasir(BisaDibayar $metode, float $jumlah): string {
    return $metode->bayar($jumlah);   // kasir hanya kenal kontraknya!
}

echo prosesKasir(new Tunai(), 50000);
echo prosesKasir(new Qris(), 50000);

Fungsi prosesKasir memakai type hint interface: ia menerima apa pun yang mengimplementasikan BisaDibayar, termasuk metode pembayaran yang belum ada saat kasir ditulis. Inilah polymorphism: satu pintu, banyak wujud.

php
<?php
// Contoh 2: abstract class dengan campuran jadi dan sketsa
abstract class Notifikasi {
    public function __construct(protected string $tujuan) {}

    // Sudah jadi: dipakai semua anak apa adanya
    public function log(): string {
        return "[" . date("H:i") . "] ke " . $this->tujuan;
    }

    // Sketsa: tiap anak WAJIB mengisi caranya sendiri
    abstract public function kirim(string $pesan): bool;
}

class NotifWa extends Notifikasi {
    public function kirim(string $pesan): bool {
        return true;   // panggil API WA di sini
    }
}

// $n = new Notifikasi("x");  // ERROR: abstract tidak bisa di-new!

Perbedaan kunci: interface tidak boleh punya isi method atau property (murni kontrak), abstract class boleh punya keduanya. Class bisa implements banyak interface, tapi hanya extends satu class (termasuk abstract).

Jebakan yang Sering Terjadi

Interface dengan satu implementasi. Membuat interface untuk class yang tidak akan pernah punya "saudara" adalah over-engineering. Buat interface saat ada dua atau lebih class yang perlu dipertukarkan, atau saat menulis library yang dipakai orang lain.

Lupa semua method abstract harus diimplementasikan. Class anak yang tidak mengisi satu pun method abstract otomatis menjadi abstract juga (tidak bisa di-new). PHP akan error jelas, tapi membingungkan kalau tidak tahu aturannya.

Interface Bawaan PHP

PHP punya interface bawaan yang membuat class-mu "berperilaku seperti" tipe native:

php
<?php
// Countable: count($objek) berfungsi!
class Keranjang implements Countable {
    private array $items = [];
    public function tambah(string $item): void { $this->items[] = $item; }
    public function count(): int { return count($this->items); }
}

$k = new Keranjang();
$k->tambah("Kopi");
echo count($k);   // 1: objek bisa di-count!

// IteratorAggregate: foreach($objek) berfungsi!
class Katalog implements IteratorAggregate {
    public function __construct(private array $produk) {}
    public function getIterator(): Traversable {
        return new ArrayIterator($this->produk);
    }
}
foreach (new Katalog(["a", "b"]) as $p) echo "$p\n";

Countable, IteratorAggregate, ArrayAccess, dan JsonSerializable adalah empat interface bawaan paling berguna. Mengimplementasikannya membuat class terasa seperti bagian alami bahasa.

Catatan teknis: Sejak PHP 8, interface boleh punya konstanta dan method dengan default implementation? Tidak: default implementation tetap milik abstract class/trait. Interface PHP murni kontrak (plus konstanta). Untuk "kontrak + isi bawaan", kombinasikan interface dengan trait.

Tantangan

Interface pembayaran

Buat interface BisaDibayar dengan method bayar($jumlah): bool. Implementasikan di Tunai (selalu true) dan Dompet (cek saldo, kurangi jika cukup). Buat fungsi checkout(BisaDibayar $metode, $total) dan uji keduanya.