Panduan Lengkap OOP di PHP: Dari Class Dasar hingga Namespace dan Polymorphism

Object-Oriented Programming (OOP) adalah paradigma pemrograman yang mengorganisir kode menjadi objek-objek yang saling berinteraksi. Dalam tutorial ini, Anda akan mempelajari konsep dasar hingga lanjutan OOP di PHP dengan contoh praktis yang mudah dipahami.

Apa itu Object-Oriented Programming?

Object-Oriented Programming (OOP) atau Pemrograman Berorientasi Objek adalah metode pemrograman yang memodelkan program menjadi objek-objek. Dalam OOP, kita menggunakan class sebagai template untuk membuat objek-objek yang memiliki karakteristik dan perilaku tertentu.

Empat Pilar Utama OOP

1. Abstraction

Menyembunyikan detail internal dan menampilkan hanya fungsi penting

2. Encapsulation

Membungkus data dan metode dalam satu unit yang terlindungi

3. Inheritance

Mewarisi properti dan metode dari class lain

4. Polymorphism

Kemampuan objek untuk mengambil banyak bentuk

Mengapa Menggunakan OOP?

Ada beberapa alasan kuat mengapa OOP menjadi pilihan utama dalam pengembangan aplikasi modern:

  • Struktur yang Jelas: Aplikasi menjadi lebih terorganisir dan mudah dipahami
  • DRY Principle (Don't Repeat Yourself): Menghindari pengulangan kode yang tidak perlu
  • Reusability: Kode dapat digunakan kembali untuk berbagai keperluan
  • Maintainability: Lebih mudah untuk melakukan pemeliharaan dan update kode
  • Scalability: Memudahkan pengembangan aplikasi yang lebih besar

Class dan Object di PHP

Sebelum memulai coding, penting untuk memahami perbedaan antara Class dan Object:

Class: Template atau blueprint untuk membuat objek. Class mendefinisikan struktur data dan perilaku yang akan dimiliki objek.

Object: Instance atau implementasi konkret dari sebuah class. Dari satu class, kita bisa membuat banyak objek.

Analogi sederhana: Jika Class adalah blueprint rumah, maka Object adalah rumah yang sebenarnya yang dibangun berdasarkan blueprint tersebut. Dari satu blueprint, kita bisa membangun banyak rumah.

Contoh Implementasi Class dan Object

Mari kita lihat contoh sederhana pembuatan class Character untuk game:

character.php
<?php
class Character {
    // Mendefinisikan atribut
    public $name;
    public $health;
    
    // Metode untuk mendapatkan nama
    function getName() {
        return $this->name;
    }
    
    // Metode untuk mendapatkan health
    function getHealth() {
        return $this->health;
    }
    
    // Metode untuk menerima damage
    function getHit($damage = 1) {
        $this->health -= $damage;
    }
}
?>
main.php
<?php
require_once("character.php");

// Membuat object dari class Character
$warrior = new Character();

// Mengatur atribut object
$warrior->name = "Baraka";
$warrior->health = 100;

// Memanggil metode object
$warrior->getHit(30);

echo "{$warrior->getName()} health is now {$warrior->getHealth()}";
// Output: Baraka health is now 70
?>
Output:
Baraka health is now 70

Attributes dan Methods

Dalam OOP, setiap class memiliki dua komponen utama:

1. Attributes (Atribut)

Attributes atau Properties adalah variabel yang dimiliki oleh sebuah class. Atribut merepresentasikan karakteristik atau identitas dari objek.

Contoh: Untuk class Student, atributnya bisa berupa:

  • name (nama)
  • studentID (NIM)
  • major (jurusan)
  • grade (nilai)

2. Methods (Metode)

Methods adalah fungsi yang dimiliki oleh sebuah class. Method merepresentasikan perilaku atau aksi yang dapat dilakukan oleh objek.

Contoh: Untuk class Student, methodnya bisa berupa:

  • study() - untuk belajar
  • payTuition() - untuk membayar uang kuliah
  • academicLeave() - untuk mengambil cuti akademik

Constructor dan Destructor

PHP menyediakan metode khusus yang disebut Constructor dan Destructor untuk mengelola lifecycle objek.

Constructor (__construct)

Constructor adalah metode khusus yang otomatis dipanggil saat objek dibuat. Constructor berguna untuk menginisialisasi nilai awal atribut objek.

Keuntungan menggunakan Constructor:
  • Tidak perlu mengatur atribut satu per satu
  • Objek langsung siap digunakan setelah dibuat
  • Kode lebih bersih dan efisien

Destructor (__destruct)

Destructor adalah metode khusus yang otomatis dipanggil saat objek dihapus atau script selesai. Destructor berguna untuk membersihkan resource atau melakukan logging.

character.php - Dengan Constructor dan Destructor
<?php
class Character {
    // Mendefinisikan atribut
    public $name;
    public $health;
    
    // Constructor - dipanggil saat objek dibuat
    function __construct($name, $health = 100) {
        $this->name = $name;
        $this->health = $health;
    }
    
    // Destructor - dipanggil saat objek dihapus
    function __destruct() {
        echo "Object cleaned: {$this->name}<br/>";
    }
    
    function getName() {
        return $this->name;
    }
    
    function getHealth() {
        return $this->health;
    }
    
    function getHit($damage = 1) {
        $this->health -= $damage;
    }
}
?>
main.php - Implementasi Constructor
<?php
require_once("character.php");

// Membuat objek dengan constructor - lebih simple!
$warrior = new Character("Baraka");
$ninja = new Character("Sub Zero", 300);

// Memanggil metode
$warrior->getHit(30);
$ninja->getHit(70);

echo "{$warrior->getName()}. Health: {$warrior->getHealth()}<br/>";
echo "{$ninja->getName()}. Health: {$ninja->getHealth()}<br/>";
?>
Output:
Baraka. Health: 70
Sub Zero. Health: 230
Object cleaned: Sub Zero
Object cleaned: Baraka

Static Attributes dan Methods

PHP memungkinkan kita untuk membuat atribut dan metode yang dapat diakses tanpa perlu membuat objek menggunakan keyword static.

Perbedaan Static vs Non-Static:
  • Non-Static: Harus membuat objek terlebih dahulu dengan new
  • Static: Langsung diakses menggunakan operator :: tanpa membuat objek
Contoh Static Property
<?php
class Character {
    // Static property untuk menghitung jumlah character
    public static $characterCount = 0;
    public $name;
    public $health;
    
    public function __construct($name, $health = 100) {
        $this->name = $name;
        $this->health = $health;
        // Mengakses static property menggunakan ::
        Character::$characterCount++;
    }
}

// Membuat beberapa objek
$warrior = new Character("Warrior", 100);
$ninja = new Character("Ninja", 90);

// Mengakses static property tanpa objek
echo "Total characters created: " . Character::$characterCount;
// Output: Total characters created: 2
?>

Encapsulation - Menyembunyikan Data

Encapsulation adalah salah satu pilar fundamental OOP yang melindungi data dari akses langsung. Di PHP, kita menggunakan access modifiers untuk mengontrol akses terhadap atribut dan metode.

Tiga Access Modifiers di PHP

Access Modifier Akses dalam Class Akses dari Class Turunan Akses dari Luar Class
public ✓ Ya ✓ Ya ✓ Ya
protected ✓ Ya ✓ Ya ✗ Tidak
private ✓ Ya ✗ Tidak ✗ Tidak
character.php - Dengan Encapsulation
<?php
class Character {
    // Atribut private tidak bisa diakses dari luar
    private $name;
    private $health;
    
    public function __construct($name, $health = 100) {
        $this->name = $name;
        $this->health = $health;
    }
    
    // Method public untuk mengakses data
    public function getName() {
        return $this->name;
    }
    
    // Method private tidak bisa dipanggil dari luar
    private function getHealth() {
        return $this->health;
    }
    
    public function getHit($damage = 1) {
        $this->health -= $damage;
    }
}
?>
Perhatian: Jika Anda mencoba mengakses atribut atau metode private dari luar class, PHP akan menghasilkan error!

Getter dan Setter Methods

Untuk mengakses dan memodifikasi atribut private, kita menggunakan Getter dan Setter methods:

  • Getter (Accessor): Metode untuk mendapatkan/membaca nilai atribut
  • Setter (Mutator): Metode untuk mengubah/menulis nilai atribut
Implementasi Getter dan Setter
<?php
class Character {
    private $name;
    private $health;
    
    public function __construct($name, $health = 100) {
        $this->name = $name;
        $this->health = $health;
    }
    
    // Getter untuk name
    public function getName() {
        return $this->name;
    }
    
    // Setter untuk name
    public function setName($name) {
        $this->name = $name;
    }
    
    // Getter untuk health
    public function getHealth() {
        return $this->health;
    }
    
    // Setter untuk health dengan validasi
    public function setHealth($health) {
        if ($health >= 0) {
            $this->health = $health;
        }
    }
    
    public function getHit($damage = 1) {
        $this->health -= $damage;
    }
}

// Penggunaan
$warrior = new Character("Baraka");
$warrior->setName("Kung Lao");  // Mengubah nama
echo "Nama baru: " . $warrior->getName();
?>
Keuntungan menggunakan Getter dan Setter:
  • Dapat menambahkan validasi sebelum mengubah nilai
  • Kontrol penuh terhadap akses data
  • Dapat menambahkan logging atau tracking
  • Lebih mudah untuk di-debug

Inheritance - Pewarisan Class

Inheritance atau pewarisan adalah konsep OOP yang memungkinkan sebuah class mewarisi atribut dan metode dari class lain. Ini sangat berguna untuk menghindari pengulangan kode (DRY principle).

Terminology dalam Inheritance:

  • Parent Class (Superclass): Class yang diwarisi
  • Child Class (Subclass): Class yang mewarisi
  • Keyword extends: Digunakan untuk mewarisi class

Studi Kasus: Sistem Senjata dalam Game

Bayangkan kita sedang membuat game yang memiliki berbagai jenis senjata seperti pedang, kapak, pistol, dan busur. Semua senjata memiliki karakteristik dasar yang sama:

  • Nama senjata
  • Damage yang dihasilkan

Namun setiap jenis senjata juga memiliki karakteristik unik. Dengan inheritance, kita bisa membuat class dasar Weapon yang kemudian diwarisi oleh class-class spesifik.

weapon.php - Parent Class
<?php
class Weapon {
    protected $name;
    protected $damage;
    
    public function __construct($name = "", $damage = 0) {
        $this->name = $name;
        $this->damage = $damage;
    }
    
    public function setName($name) {
        $this->name = $name;
    }
    
    public function setDamage($damage) {
        $this->damage = max($damage, 0);
    }
    
    public function getName() {
        return $this->name;
    }
    
    public function getDamage() {
        return $this->damage;
    }
    
    public function getInfo() {
        echo "Name: {$this->name}<br/>";
        echo "Damage: {$this->damage}<br/>";
    }
}
?>
sword.php - Child Class
<?php
require_once("weapon.php");

class Sword extends Weapon {
    private $sharpness;  // Atribut tambahan khusus untuk Sword
    
    public function __construct($name, $damage, $sharpness = 0) {
        // Memanggil constructor parent
        parent::__construct($name, $damage);
        $this->sharpness = $sharpness;
    }
    
    public function setSharpness($sharpness) {
        $this->sharpness = $sharpness;
    }
    
    public function getSharpness() {
        return $this->sharpness;
    }
    
    // Override metode getInfo dari parent
    public function getInfo() {
        parent::getInfo();  // Panggil metode parent
        echo "Sharpness: {$this->sharpness}<br/>";
    }
}
?>
index.php - Implementasi
<?php
require_once("sword.php");

$saber = new Sword("Saber", 100, 100);
echo $saber->getInfo();

echo "<br/>--- Setelah di-upgrade ---<br/><br/>";

$saber->setDamage(200);
$saber->setSharpness(1000);
echo $saber->getInfo();
?>
Output:
Name: Saber
Damage: 100
Sharpness: 100

--- Setelah di-upgrade ---

Name: Saber
Damage: 200
Sharpness: 1000
Catatan Penting tentang Inheritance:
  • Gunakan protected untuk atribut yang perlu diakses oleh child class
  • Gunakan parent:: untuk memanggil metode dari parent class
  • Child class dapat menambahkan atribut dan metode baru
  • Child class dapat meng-override (menimpa) metode parent

Polymorphism - Banyak Bentuk

Polymorphism berasal dari bahasa Yunani yang berarti "banyak bentuk". Dalam OOP, polymorphism adalah kemampuan objek yang berbeda untuk merespons metode yang sama dengan cara yang berbeda.

Dua Jenis Polymorphism

Jenis Nama Lain Karakteristik
Overloading Static/Compile-Time Polymorphism Metode dengan nama sama dalam satu class, parameter berbeda
Overriding Dynamic/Run-Time Polymorphism Metode dengan nama dan parameter sama di class berbeda
Catatan: PHP tidak mendukung method overloading secara native seperti bahasa pemrograman lain (Java, C++). Namun, kita bisa menggunakan teknik lain atau fokus pada method overriding.

Studi Kasus: Senjata Projectile

Mari kita buat contoh polymorphism dengan senjata yang menembakkan proyektil. Ada dua jenis senjata projektil:

  • Bow (Busur): Menembakkan 1 arrow per tembakan
  • Gun (Pistol): Menembakkan 2 bullets per tembakan (burst mode)

Kedua senjata ini memiliki metode shoot() yang sama, tetapi implementasinya berbeda!

projectile.php - Abstract Class
<?php
abstract class Projectile {
    protected $ammo;
    
    // Abstract methods - harus diimplementasikan oleh child class
    abstract public function getAmmo();
    abstract public function setAmmo($ammo);
    abstract public function shoot();
}
?>
bow.php - Class Bow
<?php
require_once('projectile.php');

class Bow extends Projectile {
    public function getAmmo() {
        return $this->ammo;
    }
    
    public function setAmmo($ammo) {
        $this->ammo = $ammo;
    }
    
    // Implementasi shoot untuk Bow - 1 ammo per tembakan
    public function shoot() {
        $this->ammo--;
        echo "Bow menembakkan 1 arrow! 🏹<br/>";
    }
}
?>
gun.php - Class Gun
<?php
require_once('projectile.php');

class Gun extends Projectile {
    public function getAmmo() {
        return $this->ammo;
    }
    
    public function setAmmo($ammo) {
        $this->ammo = $ammo;
    }
    
    // Implementasi shoot untuk Gun - 2 ammo per tembakan (burst)
    public function shoot() {
        $this->ammo -= 2;
        echo "Gun menembakkan 2 bullets! 🔫<br/>";
    }
}
?>
main.php - Demonstrasi Polymorphism
<?php
require('bow.php');
require('gun.php');

$bow = new Bow;
$gun = new Gun;

$bow->setAmmo(3);
$gun->setAmmo(100);

echo "=== Testing Bow ===<br/>";
$bow->shoot();
echo "Sisa ammo Bow: {$bow->getAmmo()}<br/><br/>";

echo "=== Testing Gun ===<br/>";
for ($i = 1; $i <= 10; $i++) {
    $gun->shoot();
}
echo "Sisa ammo Gun: {$gun->getAmmo()}<br/>";
?>
Output:
=== Testing Bow ===
Bow menembakkan 1 arrow! 🏹
Sisa ammo Bow: 2

=== Testing Gun ===
Gun menembakkan 2 bullets! 🔫
Gun menembakkan 2 bullets! 🔫
Gun menembakkan 2 bullets! 🔫
Gun menembakkan 2 bullets! 🔫
Gun menembakkan 2 bullets! 🔫
Gun menembakkan 2 bullets! 🔫
Gun menembakkan 2 bullets! 🔫
Gun menembakkan 2 bullets! 🔫
Gun menembakkan 2 bullets! 🔫
Gun menembakkan 2 bullets! 🔫
Sisa ammo Gun: 80
Ini adalah Polymorphism! Kedua objek (Bow dan Gun) memiliki metode shoot() yang sama, tetapi perilakunya berbeda sesuai dengan jenis senjatanya.

Abstract Class

Abstract Class adalah class khusus yang tidak dapat diinstansiasi menjadi objek. Abstract class berfungsi sebagai template untuk class lain.

Karakteristik Abstract Class:
  • Menggunakan keyword abstract
  • Tidak bisa dibuat objeknya (tidak bisa pakai new)
  • Harus diwarisi oleh class lain
  • Dapat memiliki abstract methods yang harus diimplementasikan oleh child class
  • Dapat memiliki concrete methods (metode biasa)
Contoh Abstract Class
<?php
abstract class Character {
    private $name;
    private $health;
    
    // Abstract methods - tidak ada implementasi
    abstract public function getName();
    abstract public function getHealth();
    abstract public function getHit($damage);
}

// ERROR! Tidak bisa membuat objek dari abstract class
$myChar = new Character(); // Fatal error!
?>

PHP Namespace

Namespace adalah cara untuk mengorganisir dan mengelompokkan class, interface, function, dan constant dalam PHP. Namespace sangat berguna ketika kita memiliki banyak file dengan nama class yang sama.

Mengapa Perlu Namespace?

Bayangkan Anda memiliki struktur folder seperti ini:

Struktur Folder
C:\xampp\htdocs\
├── Office\
│   └── employee.php
└── Factory\
    └── employee.php

Tanpa namespace, PHP akan bingung membedakan employee.php yang mana yang harus digunakan!

Factory/employee.php
<?php
namespace Factory;

class Employee {
    private $name;
    
    public function __construct($name) {
        $this->name = $name;
    }
    
    public function getName() {
        return $this->name;
    }
    
    public function getSalaryInfo() {
        return "Gaji dibayar harian";
    }
}
?>
Office/employee.php
<?php
namespace Office;

class Employee {
    private $name;
    
    public function __construct($name) {
        $this->name = $name;
    }
    
    public function getName() {
        return $this->name;
    }
    
    public function getSalaryInfo() {
        return "Gaji dibayar bulanan";
    }
}
?>
main.php - Menggunakan Namespace
<?php
require("Factory/employee.php");
require("Office/employee.php");

// Menggunakan namespace dengan backslash
$empOffice = new \Office\Employee("John");
$empFactory = new \Factory\Employee("Bob");

echo "Office Employee: " . $empOffice->getName() . "<br/>";
echo "Sistem Gaji: " . $empOffice->getSalaryInfo() . "<br/><br/>";

echo "Factory Employee: " . $empFactory->getName() . "<br/>";
echo "Sistem Gaji: " . $empFactory->getSalaryInfo() . "<br/>";
?>
Output:
Office Employee: John
Sistem Gaji: Gaji dibayar bulanan

Factory Employee: Bob
Sistem Gaji: Gaji dibayar harian
Tips Namespace:
  • Namespace harus dideklarasikan di baris pertama file PHP
  • Gunakan \ untuk mengakses class dari namespace tertentu
  • Bisa menggunakan use untuk import namespace
  • Namespace biasanya mengikuti struktur folder

Best Practices dalam OOP PHP

Berikut adalah beberapa praktik terbaik yang sebaiknya Anda ikuti saat menulis kode OOP di PHP:

1. Gunakan Access Modifiers dengan Bijak

  • Gunakan private untuk atribut yang tidak perlu diakses dari luar
  • Gunakan protected untuk atribut yang perlu diwarisi
  • Gunakan public hanya untuk metode yang memang perlu diakses dari luar

2. Ikuti Prinsip SOLID

  • Single Responsibility: Satu class hanya punya satu tanggung jawab
  • Open/Closed: Terbuka untuk extension, tertutup untuk modification
  • Liskov Substitution: Child class harus bisa menggantikan parent class
  • Interface Segregation: Lebih baik banyak interface spesifik daripada satu interface general
  • Dependency Inversion: Bergantung pada abstraksi, bukan konkret

3. Gunakan Penamaan yang Deskriptif

  • Class name: PascalCase (contoh: UserController)
  • Method name: camelCase (contoh: getUserData())
  • Constant: UPPER_CASE (contoh: MAX_LOGIN_ATTEMPTS)

4. Dokumentasi dengan PHPDoc

Contoh PHPDoc
<?php
/**
 * Class untuk mengelola data user
 * 
 * @author Brian
 * @version 1.0
 */
class User {
    /**
     * Mendapatkan data user berdasarkan ID
     * 
     * @param int $id ID user yang dicari
     * @return array Data user dalam bentuk array
     */
    public function getUserById($id) {
        // implementasi...
    }
}
?>

💡 Latihan: Membuat Class Employee

Buatlah sebuah class yang mensimulasikan data karyawan di sebuah perusahaan dengan spesifikasi berikut:

Atribut yang harus ada:

  • Name (nama)
  • ID (ID karyawan)
  • Gender (jenis kelamin)
  • Salary (gaji)

Method yang harus ada:

  • getSalary() - untuk mendapatkan gaji karyawan
  • getData() - untuk mendapatkan semua data (name, ID, gender)
  • giveBonus($amount) - untuk memberikan bonus kepada karyawan

Ketentuan:

  • Gunakan encapsulation (private attributes)
  • Implementasikan getter dan setter methods
  • Gunakan constructor untuk inisialisasi
Lihat Solusi
employee.php - Solusi Latihan
<?php
class Employee {
    private $name;
    private $id;
    private $gender;
    private $salary;
    
    public function __construct($name, $id, $gender, $salary) {
        $this->name = $name;
        $this->id = $id;
        $this->gender = $gender;
        $this->salary = $salary;
    }
    
    public function getSalary() {
        return $this->salary;
    }
    
    public function getData() {
        return [
            'name' => $this->name,
            'id' => $this->id,
            'gender' => $this->gender
        ];
    }
    
    public function giveBonus($amount) {
        $this->salary += $amount;
        echo "Bonus Rp " . number_format($amount) . " telah diberikan!<br/>";
    }
    
    // Getter methods
    public function getName() {
        return $this->name;
    }
    
    public function getId() {
        return $this->id;
    }
    
    public function getGender() {
        return $this->gender;
    }
    
    // Setter methods
    public function setName($name) {
        $this->name = $name;
    }
    
    public function setSalary($salary) {
        if ($salary >= 0) {
            $this->salary = $salary;
        }
    }
}

// Penggunaan
$emp = new Employee("Budi Santoso", "EMP001", "Laki-laki", 5000000);

echo "Nama: " . $emp->getName() . "<br/>";
echo "Gaji: Rp " . number_format($emp->getSalary()) . "<br/><br/>";

$emp->giveBonus(1000000);
echo "Gaji setelah bonus: Rp " . number_format($emp->getSalary()) . "<br/>";
?>

Kesimpulan

Object-Oriented Programming (OOP) adalah paradigma pemrograman yang sangat powerful dan penting untuk dikuasai. Dengan memahami konsep-konsep dasar seperti Class, Object, Encapsulation, Inheritance, dan Polymorphism, Anda dapat menulis kode yang lebih terstruktur, maintainable, dan reusable.

Ringkasan Konsep OOP:
  • Class: Template untuk membuat objek
  • Object: Instance dari class
  • Encapsulation: Menyembunyikan data dengan access modifiers
  • Inheritance: Mewarisi properti dari class lain
  • Polymorphism: Kemampuan objek mengambil banyak bentuk
  • Abstraction: Menyembunyikan kompleksitas implementasi

Referensi dan Sumber Belajar

Komentar