Abstract Class vs Interface di PHP — Kapan Pakai yang Mana?

Kalau kamu baru belajar OOP, dua hal ini pasti bakal bikin kepala pusing duluan — bukan karena susah, tapi karena hampir semua penjelasan di internet langsung loncat ke kode tanpa ngasih gambaran besarnya dulu.

Jadi di artikel ini, kita mulai dari analogi dulu. Baru setelah ngerti konsepnya, kita masuk ke kode.

Masalah yang Mereka Selesaikan

Bayangin kamu lagi bangun aplikasi manajemen data. Di aplikasi ini ada fitur ekspor laporan, dan kamu mau support tiga format: PDF, Excel, dan CSV.

Masing-masing format cara pembuatannya beda. Tapi semua harus bisa melakukan dua hal yang sama:

  • generate() — untuk membuat file-nya
  • download() — untuk mengirim file ke browser

Nah, di sinilah masalahnya muncul.

Kalau kamu bikin 3 kelas terpisah tanpa aturan apapun, bisa-bisa PdfExporter punya method bernama generate(), tapi ExcelExporter malah namanya build(), dan CsvExporter namanya create(). Semua bikin hal yang sama tapi dengan nama berbeda.

Ini yang disebut kode tidak konsisten — dan ini mimpi buruk kalau aplikasi makin besar.

Interface dan Abstract Class adalah solusinya. Keduanya adalah cara untuk bilang ke semua kelas: “Hei, kamu harus punya method ini. Titik.”

Interface — “Daftar Aturan Kosong”

Analoginya
Bayangin Interface seperti formulir kontrak kerja.
Di kontrak itu tertulis: “Kamu wajib bisa menyetir mobil, wajib bisa berbahasa Inggris, dan wajib punya SIM A.”
Kontrak tidak peduli kamu belajar nyetir dari siapa, les bahasa Inggris di mana, atau ujian SIM-nya di kota mana. Yang penting: hasilnya ada, dan bisa dibuktikan.

Interface di PHP persis seperti itu — dia cuma mendaftarkan apa yang harus bisa dilakukan, tanpa peduli bagaimana caranya.

Aturan Interface

  • Semua method di dalam interface tidak boleh punya isi (hanya nama dan parameternya saja).
  • Kelas yang pakai interface (implements) wajib mengisi semua method yang didaftarkan.
  • Satu kelas boleh implements lebih dari satu interface sekaligus.

Kodenya

<?php

namespace App\Contracts;

interface ExporterInterface
{
    // Hanya daftar method — tidak ada isi sama sekali
    public function generate(array $data): string;
    public function download(string $filename): void;
}

📌 Perhatikan — di dalam interface tidak ada kurung kurawal { } berisi logika. Hanya ada nama method, parameter, dan tipe return-nya. Itu saja. Ini namanya method signature.

Abstract Class — “Fondasi Setengah Jadi”

Analoginya

Sekarang bayangin Abstract Class seperti rumah yang sudah dibangun setengah jadi oleh developer perumahan.
Fondasi sudah ada. Dinding sudah ada. Atap sudah ada. Listrik dan air sudah nyambung.
Tapi beberapa hal sengaja dibiarkan kosong — desain interior, warna cat, jenis lantai. Kenapa? Karena itu terserah pembelinya masing-masing.

Developer bilang: “Bagian ini kamu yang harus isi sendiri. Bagian lainnya sudah aku siapkan, tinggal pakai.”

Abstract Class di PHP persis seperti itu.

Aturan Abstract Class

  • Boleh punya method yang sudah ada isinya (siap pakai oleh subclass).
  • Boleh punya method abstract — yang wajib diisi oleh subclass.
  • Boleh punya property (variabel seperti $disk).
  • Tidak bisa di-new langsung — hanya bisa dipakai lewat subclass.
  • Satu kelas hanya boleh extendssatu abstract class.

Kodenya

<?php

namespace App\Services;

use App\Contracts\ExporterInterface;
use Illuminate\Support\Facades\Storage;

abstract class BaseExporter implements ExporterInterface
{
    // Property yang bisa dipakai oleh semua subclass
    protected string $disk = 'local';

    // Method ini SUDAH ADA ISINYA — semua format pakai logika download yang sama:
    // simpan file sementara → kirim ke browser → hapus file
    public function download(string $filename): void
    {
        $path = "exports/{$filename}";

        Storage::disk($this->disk)->put($path, $this->tempFile);

        response()->download(
            Storage::disk($this->disk)->path($path)
        )->deleteFileAfterSend()->send();
    }

    // Method ini SENGAJA DIKOSONGKAN — tiap format cara generate-nya beda:
    // PDF pakai library berbeda dengan Excel, Excel berbeda dengan CSV
    abstract public function generate(array $data): string;
}

📌 Perhatikan keyword abstract di depan method generate. Itu artinya: “Method ini wajib diisi oleh siapapun yang extends class ini. Kalau tidak diisi, PHP akan langsung error.”

Concrete Class — “Produk Jadinya”

Ini adalah kelas yang benar-benar dipakai di aplikasi. Dia extends abstract class dan mengisi semua method yang masih kosong.

📁 app/Services/PdfExporter.php

<?php

namespace App\Services;

use Barryvdh\DomPDF\Facade\Pdf;

class PdfExporter extends BaseExporter
{
    // Wajib diisi karena BaseExporter mendeklarasikannya sebagai abstract
    public function generate(array $data): string
    {
        // Logika khusus PDF — pakai library DomPDF
        $pdf = Pdf::loadView('exports.report', ['data' => $data]);

        $this->tempFile = $pdf->output();

        return $this->tempFile;
    }

    // Method download() tidak perlu ditulis ulang —
    // sudah diwarisi dari BaseExporter
}

📁 app/Services/ExcelExporter.php

<?php

namespace App\Services;

use Maatwebsite\Excel\Facades\Excel;

class ExcelExporter extends BaseExporter
{
    public function generate(array $data): string
    {
        // Logika khusus Excel — pakai library Maatwebsite
        // Cara generate-nya sama sekali beda dengan PDF, tapi nama method-nya sama
        $this->tempFile = Excel::raw(new ReportExport($data), \Maatwebsite\Excel\Excel::XLSX);

        return $this->tempFile;
    }
}

📌 ExcelExporter tidak perlu nulis ulang download() karena sudah diwarisi dari BaseExporter. Yang berbeda hanya cara generate()-nya — dan memang itulah satu-satunya yang berbeda antar format.

Gambaran Besar — Posisi Tiap File

Ini gambaran keseluruhan struktur dan hubungannya:

app/
├── Contracts/
│   └── ExporterInterface.php   ← "Daftar aturan" — interface
│
├── Services/
│   ├── BaseExporter.php        ← "Fondasi" — abstract class
│   ├── PdfExporter.php         ← "Produk jadi" — concrete class
│   ├── ExcelExporter.php       ← concrete class lain
│   └── CsvExporter.php         ← concrete class lain

Alur warisannya:

ExporterInterface
      ↓ (implements)
BaseExporter
      ↓ (extends)
PdfExporter  /  ExcelExporter  /  CsvExporter

Jadi, Kapan Pakai Yang Mana?

Pakai Interface ketika: Kamu cuma mau bilang “kelas ini harus bisa melakukan X dan Y” — tanpa peduli bagaimana caranya dan tanpa ada logika bersama sama sekali.

Pakai Abstract Class ketika: Ada logika yang sama persis di beberapa kelas, dan kamu malas nulis ulang di tiap kelas. Tapi beberapa bagian tetap harus diisi sendiri.

Pakai keduanya (paling umum di Laravel) ketika: Interface sebagai kontrak publik, Abstract Class sebagai fondasi bersama, Concrete Class sebagai implementasi spesifik.

📊 Perbandingan Singkat

InterfaceAbstract Class
Bisa punya isi method
Bisa punya property
Satu kelas bisa pakai lebih dari satu
Bisa di-new langsung
Tujuan utamaKontrak/aturanFondasi bersama
💡 Ingat!
Waktu pertama kali belajar ini, wajar kalau masih bingung “kenapa tidak bikin class biasa saja?”

Jawabannya: boleh saja. Tapi begitu aplikasi makin besar dan kamu punya banyak format ekspor — atau besok diminta tambah format baru — kamu akan sangat bersyukur ada interface yang memastikan semuanya konsisten, dan ada abstract class yang memastikan kamu tidak copy-paste logika download() yang sama ke mana-mana.
— Ini bukan tentang benar atau salah. Ini tentang kode yang mudah dirawat jangka panjang.
Share Twitter/X LinkedIn