Hierarki yang kita bangun sejauh ini bersifat opsional: child bebas menimpa method atau membiarkannya apa adanya. Kadang kita membutuhkan jaminan yang lebih keras — semua bentuk wajib punya method luas(), semua hewan wajib bisa bersuara(). Abstract class mengisi kebutuhan tersebut: class setengah jadi yang tidak bisa diinstansiasi langsung, melainkan mewajibkan child menyelesaikan bagian yang belum diisi. Chapter ini membahas cara kerja abstract class, pola desain template method yang dibangun di atasnya, dan posisi abstract class dibandingkan interface.
Class Abstract Tidak Bisa Diinstansiasi #
Kata kunci abstract di depan deklarasi class menandakan class tersebut belum lengkap dan dilarang dijadikan object. Percobaan instansiasi langsung ditolak oleh PHP:
<?php
abstract class BentukDasar
{
abstract public function luas(): float;
}
$bentuk = new BentukDasar();Output yang Dihasilkan:
Fatal error: Uncaught Error: Cannot instantiate abstract class BentukDasar in /home/z/my-project/php-course/content/php/object-oriented-programming/abstract-classes/.verify_tmp.php:7
Stack trace:
#0 {main}
thrown in /home/z/my-project/php-course/content/php/object-oriented-programming/abstract-classes/.verify_tmp.php on line 7Pesan Cannot instantiate abstract class muncul karena BentukDasar memang bukan produk jadi — class tersebut mendeklarasikan abstract public function luas() tanpa isi, menandakan setiap bentuk konkret wajib menyuplai caranya sendiri menghitung luas. Meski begitu, abstract class tetap boleh memiliki property, constructor, dan method berisi kode lengkap; yang dilarang hanyalah operasi new pada class abstract itu sendiri.
Abstract Method Memaksa Child Berimplementasi #
Abstract method adalah deklarasi tanpa isi — hanya signature yang diakhiri titik koma, tanpa kurung kurawal. Child konkret wajib mengimplementasikan seluruh abstract method milik parent, dan PHP memeriksa kewajiban itu sejak class dideklarasikan:
<?php
abstract class Hewan
{
protected string $nama;
public function __construct(string $nama)
{
$this->nama = $nama;
}
abstract public function bersuara(): string;
public function perkenalan(): string
{
// Method konkret boleh memanggil abstract method dengan aman,
// karena setiap child pasti mengimplementasikannya.
return "{$this->nama} berbunyi: " . $this->bersuara();
}
}
class Kucing extends Hewan
{
public function bersuara(): string
{
return "Meong";
}
}
class Sapi extends Hewan
{
public function bersuara(): string
{
return "Moo";
}
}
echo (new Kucing("Oyen"))->perkenalan() . "\n";
echo (new Sapi("Molly"))->perkenalan() . "\n";Output yang Dihasilkan:
Oyen berbunyi: Meong
Molly berbunyi: MooMari kita bedah strukturnya:
abstract public function bersuara(): string;— deklarasi kontrak tanpa isi; setiap child menentukan sendiri suaranya.perkenalan()adalah method konkret di dalam class abstract — boleh berisi kode dan diwarisi child apa adanya.KucingdanSapimengisibersuara()versi masing-masing dengan signature yang sama.
Perhatikan kekuatan kombinasinya: perkenalan() milik parent memanggil $this->bersuara() dengan tenang, karena PHP menjamin setiap child pasti memiliki method tersebut. Kontrak sebagian sudah dikerjakan parent (cara memperkenalkan diri), sedangkan sebagian didelegasikan (suara hewan). Jika child lupa mengimplementasikan abstract method, kegagalan muncul sejak deklarasi:
<?php
abstract class Hewan
{
abstract public function bersuara(): string;
}
class Ikan extends Hewan
{
// Tidak mengimplementasikan bersuara().
}Output yang Dihasilkan:
Fatal error: Class Ikan contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Hewan::bersuara) in /home/z/my-project/php-course/content/php/object-oriented-programming/abstract-classes/.verify_tmp.php on line 7Pesan contains 1 abstract method and must therefore be declared abstract or implement the remaining methods memberikan dua jalan keluar: jadikan Ikan abstract juga (berarti Ikan ikut dilarang diinstansiasi), atau lengkapi method bersuara(). Error ini terjadi ketika class dimuat, sehingga kelalaian tertangkap jauh sebelum pemanggilan method berlangsung di produksi.
Template Method Pattern #
Kekuatan sesungguhnya abstract class adalah menggabungkan kerangka alur yang tetap dengan langkah-langkah yang bebas berubah — pola desain klasik bernama template method. Parent menyusun algoritma utuh, lalu menyerahkan langkah spesifik kepada child:
<?php
abstract class EksporLaporan
{
// Template method: kerangka alur dikunci di parent.
final public function ekspor(array $data): string
{
$isi = $this->formatHeader($data);
$isi .= $this->formatBaris($data);
$isi .= $this->formatFooter($data);
return $isi;
}
abstract protected function formatHeader(array $data): string;
abstract protected function formatBaris(array $data): string;
abstract protected function formatFooter(array $data): string;
}
class EksporCsv extends EksporLaporan
{
protected function formatHeader(array $data): string
{
return implode(";", array_keys($data)) . "\n";
}
protected function formatBaris(array $data): string
{
return implode(";", $data) . "\n";
}
protected function formatFooter(array $data): string
{
return ""; // Format CSV tidak memakai footer.
}
}
class EksporTeks extends EksporLaporan
{
protected function formatHeader(array $data): string
{
return "LAPORAN\n-------\n";
}
protected function formatBaris(array $data): string
{
$baris = "";
foreach ($data as $kunci => $nilai) {
$baris .= "{$kunci}: {$nilai}\n";
}
return $baris;
}
protected function formatFooter(array $data): string
{
return "-------\nSelesai\n";
}
}
$dataPenjualan = ["Produk" => "Buku PHP", "Jumlah" => 3, "Total" => 285000];
echo (new EksporCsv())->ekspor($dataPenjualan);
echo "---\n";
echo (new EksporTeks())->ekspor($dataPenjualan);Output yang Dihasilkan:
Produk;Jumlah;Total
Buku PHP;3;285000
---
LAPORAN
-------
Produk: Buku PHP
Jumlah: 3
Total: 285000
-------
SelesaiMari kita urai perannya:
ekspor()adalah template method: urutan header → baris → footer dikunci di parent, dan child tidak bisa mengubah urutannya.- Kata kunci
finalpadaekspor()mencegah child menimpa method tersebut — alur inti memang tidak boleh diganti turunannya (pembahasan lengkap ada di Chapter Final Classes dan Methods ). - Tiga abstract method
protectedmenjadi titik kustomisasi; setiap format ekspor mengisinya sendiri tanpa menyentuh alur. EksporCsvdanEksporTeksmenghasilkan keluaran berbeda dari kerangka yang persis sama.
Dua object menghasilkan format yang berbeda tanpa menyentuh satu baris pun dari alur ekspor. Ketika kebutuhan alur berubah — misalnya perlu validasi data sebelum menulis — cukup method ekspor() di parent yang disunting, dan semua format turunannya otomatis mengikuti.
Abstract Class vs Interface #
Abstract class dan interface sama-sama menyusun kontrak, tetapi keduanya mengisi ceruk yang berbeda. Tabel berikut merangkum perbedaan pokoknya:
| Aspek | Abstract Class | Interface |
|---|---|---|
| Instansiasi langsung | Tidak bisa | Tidak bisa |
| Method berisi kode | Bisa (method konkret) | Tidak, hanya deklarasi |
| Property | Bisa menyimpan property | Tidak bisa, hanya constant |
| Constructor | Bisa dimiliki | Tidak bisa |
| Jumlah per class | Satu parent abstract per child | Banyak interface sekaligus |
| Relasi yang diekspresikan | is-a dalam satu keluarga turunan | can-do lintas keluarga class |
Abstract class unggul ketika sekelompok class berbagi state dan alur yang sama — keluarga Hewan di atas berbagi property $nama dan method perkenalan(), sehingga duplikasi terpangkas. Interface unggul ketika kemampuan yang sama dipinjam oleh class dari keluarga berbeda, misalnya kemampuan JsonSerializable yang bisa diimplementasi entitas apa pun. Keduanya bahkan sering dipakai bersamaan: interface mendefinisikan kemampuan, abstract class menyediakan implementasi dasarnya. Karena satu child hanya boleh memiliki satu parent abstract tetapi boleh mengimplementasikan banyak interface, kombinasi keduanya menjadi alat utama menyusun kontrak pada aplikasi besar.
Ringkasan #
Abstract class mengunci kerangka sekaligus menyerahkan detail: class abstract tidak bisa diinstansiasi, abstract method memaksa child berimplementasi sejak class dideklarasikan, dan template method menjaga alur tetap berada di satu tempat. Dibandingkan interface, abstract class membawa state dan method konkret — kontrak parsial yang memangkas duplikasi di dalam satu keluarga class. Setelah kontrak parsial ini dikuasai, langkah berikutnya adalah kontrak penuh yang bisa dipinjam lintas keluarga: interface menjadi topik chapter berikutnya.