π§© Object-Oriented Programming Deep Dive β Dari Prinsip ke Praktik
Bedah OOP bukan sekadar 4 pilar (encapsulation, inheritance, polymorphism, abstraction) β tapi mengapa paradigma ini lahir, kapan cocok, dan kapan justru salah alat. Termasuk perbandingan dengan FP (functional programming), masalah inheritance vs composition, SOLID, dan trade-off di dunia nyata. Untuk peta paradigma, lihat hierarchy-software-engineering-paradigm. Untuk pola desain, lihat design-patterns-gof.
Daftar Isi
- 1. Kenapa OOP Ada β Masalah yang Mau Diselesaikan
- 2. Empat Pilar β Anatomi Sebenarnya
- 3. Inheritance vs Composition β Kontroversi Abadi
- 4. SOLID β Lima Prinsip yang (Sering) Disalahpahami
- 5. OOP vs FP β Bukan Perang, Tapi Spektrum
- 6. Polimorfisme dalam Praktek
- 7. Anti-Pattern yang Umum
- 8. Kapan OOP Justru Salah
- 9. Roadmap Belajar
1. Kenapa OOP Ada β Masalah yang Mau Diselesaikan
1.1 Sejarah Singkat
1950s: Assembly β no abstraction
1960s: Procedural (C) β fungsi + data terpisah
1970s: Simula 67 β konsep "class" pertama (simulasi)
1980s: Smalltalk β OOP murni, message passing
1985+: C++ β OOP + performa
1990s: Java β OOP "wajib", enterprise adoption
2000s+: Python/Ruby/JS β OOP fleksibel + FP features
2010s+: Rust/Go β composition-first, OOP "tanpa inheritance"Masalah yang OOP jawab: prosedural gagal saat program > 100K LOC β data terpisah dari fungsi β siapa yang ubah state ini? Siapa yang validasi? OOP bind state + behavior dalam satu unit (object) β modularity, reusability, maintainability.
1.2 Analogi Mental yang Tepat
BUKAN: "OOP = dunia nyata, class Kucing turunan class Hewan"
TAPI: "OOP = kontrak + state + behavior dalam satu boundary"
βββββββββββββββββββββββββββββββ
β BankAccount (boundary) β
β βββββββββββββββββββββββββ β
β state: balance: f64 β
β behavior: deposit(x) β
β withdraw(x) β
β get_balance() β
β βββββββββββββββββββββββββ β
β invariant: balance >= 0 β
βββββββββββββββββββββββββββββββ
β²
β 1 class = 1 boundary
β invariant dijaga di dalamKunci: OOP itu tentang boundary + invariant, bukan tentang meniru alam.
2. Empat Pilar β Anatomi Sebenarnya
2.1 Encapsulation β Bukan Sekadar private
// β Salah paham: private field = encapsulation
public class User {
private String name;
public String getName() { return name; }
public void setName(String n) { this.name = n; } // setter tanpa validasi!
}
// β
Encapsulation = invariant dijaga
public class User {
private String name;
public User(String name) {
if (name == null || name.isBlank())
throw new IllegalArgumentException("Name required");
this.name = name;
}
public String getName() { return name; }
// TIDAK ADA setName β name immutable setelah konstruksi
}Encapsulation = menyembunyikan representasi internal + menjaga invariant di satu tempat. Getter/setter generik BUKAN encapsulation β itu field publik berjubah.
2.2 Inheritance β Pewarisan dengan Hati-Hati
// Kapan inheritance OK:
// 1. IS-A benar-benar berlaku (subclass substitutable)
// 2. Behavioral extension (template method)
// 3. Shared immutable contract
abstract class PaymentProcessor {
public final Result process(Payment p) {
validate(p); // hook
Result r = execute(p); // abstract β subclass implement
notify(p); // hook
return r;
}
abstract Result execute(Payment p);
}2.3 Polymorphism β Satu Interface, Banyak Perilaku
interface PaymentMethod {
void pay(Money amount);
}
class CreditCard implements PaymentMethod { public void pay(Money m) { /* cc logic */ } }
class GoPay implements PaymentMethod { public void pay(Money m) { /* gopay logic */ } }
class QRIS implements PaymentMethod { public void pay(Money m) { /* qris logic */ } }
// Caller gak perlu tau detail β DIP (Dependency Inversion)
void checkout(PaymentMethod method, Money amount) {
method.pay(amount); // dispatch dinamis
}2.4 Abstraction β Kontrak, Bukan Implementasi
Abstraction = "apa yang dilakukan" (contract)
Implementation = "bagaimana melakukannya"
Client code β interface β implementation
β β β
checkout() PaymentMethod CreditCard.goPay()3. Inheritance vs Composition β Kontroversi Abadi
3.1 Masalah Klasik Inheritance
// β Deep hierarchy β fragile, rigid, opaque
class Bird { void fly() {} }
class Penguin extends Bird {} // Penguin gak bisa fly! LSP violation
class Ostrich extends Bird {} // sama
// β Diamond problem (C++)
class A { void f() {} }
class B extends A {}
class C extends A {}
class D extends B, C {} // f() mana yang dipakai?βFavor composition over inheritanceβ (GoF):
// β
Composition
class Penguin {
private final Movement movement; // inject behavior
Penguin(Movement m) { this.movement = m; }
}
interface Movement { void move(); }
class Swim implements Movement { public void move() { /* swim */ } }
class Fly implements Movement { public void move() { /* fly */ } }
class Walk implements Movement { public void move() { /* walk */ } }3.2 Matrix Keputusan
| Skenario | Pilih | Kenapa |
|---|---|---|
| Subclass memang subtype (Cat β Animal) + behavior extends | Inheritance | IS-A valid, template method |
| Cuma mau pakai ulang method | Composition | Decouple, testable |
| Behavior berubah runtime | Composition | Strategy pattern |
| Deep hierarchy (>3 level) | Composition | Fragile base class |
| Framework callback (Android Activity, React component) | Inheritance (forced) | Framework contract |
4. SOLID β Lima Prinsip yang (Sering) Disalahpahami
| Prinsip | Nama | Inti | Sering Disalahpahami Sebagai |
|---|---|---|---|
| S | Single Responsibility | 1 class = 1 alasan berubah | β1 class = 1 fungsiβ β |
| O | Open/Closed | Extend tanpa modify | βHarus pakai inheritanceβ β |
| L | Liskov Substitution | Subclass substitutable | βSubclass harus punya semua methodβ β |
| I | Interface Segregation | Client jangan dipaksa depend ke method yang gak dipakai | β1 method per interfaceβ β |
| D | Dependency Inversion | Depend ke abstraction, bukan concrete | βHarus pakai DI frameworkβ β |
4.1 S β Contoh Benar
// β 3 alasan berubah: format, persistence, validation
class ReportService {
void generateReport() { /* format */ }
void saveToDb() { /* persistence */ }
void validate() { /* validation */ }
}
// β
3 class, 3 alasan berubah
class ReportFormatter { String format(Data d); }
class ReportRepository { void save(Report r); }
class ReportValidator { void validate(Data d); }4.2 L β Contoh Benar
// LSP: subclass harus bisa menggantikan parent tanpa break behavior
class Rectangle { void setWidth(int w); void setHeight(int h); }
class Square extends Rectangle { // β Square setWidth juga ubah height β LSP violation
void setWidth(int w) { super.setWidth(w); super.setHeight(w); }
}
// β
Solusi: jangan paksa hierarki. Square bukan Rectangle (dari sisi mutable behavior)
class Shape { abstract int area(); }
class Rectangle extends Shape { /* w,h */ }
class Square extends Shape { /* s */ }5. OOP vs FP β Bukan Perang, Tapi Spektrum
| Aspek | OOP | Functional |
|---|---|---|
| State | Mutable, encapsulated | Immutable, explicit |
| Unit | Object (state+behavior) | Function (pure) |
| Data flow | Message passing | Pipeline / composition |
| Side effects | Dikendalikan per class | Dikarantina (IO monad) |
| Concurrency | Shared state + lock | Immutable β lock-free |
| Testing | Mock dependency | Pure function = trivial |
| Bahasa | Java, C++, Python, Ruby | Haskell, Elixir, Clojure |
| Hybrid | β | Rust, Scala, Kotlin, JS/TS, modern Python |
Realita 2026: Hampir semua codebase produksi hybrid. Contoh: Rust = struct + trait (OOP-ish) tapi ownership/borrow (FP-ish); TypeScript = class + functional utility; Python = class + dataclass + functools.
6. Polimorfisme dalam Praktek
6.1 Dispatch Mechanism
Static dispatch (compile-time):
C++ templates, Rust generics, Java generics (erasure)
β Zero overhead, tapi binary bloat
Dynamic dispatch (runtime):
C++ virtual, Java interface, Rust trait objects (dyn Trait)
β vtable lookup, 1 indirection, fleksibel
6.2 Contoh Rust β Trait (OOP Tanpa Inheritance)
trait Payment {
fn pay(&self, amount: u64);
}
struct CreditCard { number: String }
impl Payment for CreditCard {
fn pay(&self, amount: u64) { /* cc */ }
}
struct GoPay { balance: u64 }
impl Payment for GoPay {
fn pay(&self, amount: u64) { /* gopay */ }
}
// Static dispatch (generics)
fn checkout_static<P: Payment>(p: &P, amount: u64) { p.pay(amount); }
// Dynamic dispatch (trait object)
fn checkout_dyn(p: &dyn Payment, amount: u64) { p.pay(amount); }7. Anti-Pattern yang Umum
| Anti-Pattern | Gejala | Fix |
|---|---|---|
| God Object | 1 class ngurus segalanya | SRP β pecah |
| Bean Class | Class cuma getter/setter tanpa behavior | Domain logic masuk ke class |
| Deep Hierarchy | 5+ level inheritance | Composition |
| Circle Dependency | AβBβA | Interface + DI container |
| Anemic Domain Model | Entity tanpa method, logic di service | Domain model enrichment |
| Premature Abstraction | Interface 1 implementasi | YAGNI β tambah interface saat butuh 2 impl |
| Sequential Coupling | Method harus dipanggil urutan tertentu | Builder pattern / state machine |
8. Kapan OOP Justru Salah
β
OOP cocok:
- Domain kompleks dengan state + invariant (bank, e-commerce, game)
- Long-lived codebase yang butuh maintainability
- Tim besar dengan boundary jelas
β OOP berlebihan:
- Script/CLI sekali pakai β prosedural cukup
- Data pipeline / ETL β FP pipeline lebih jelas
- High-performance hot loop β struct of arrays, bukan object graph
- Stateless microservice β function handler cukup9. Roadmap Belajar
L1: Syntax (class, object, method) β buat CRUD sederhana
L2: 4 pilar + getter/setter vs encapsulation β refactor kode lama
L3: Composition vs inheritance β pola Strategy, Template
L4: SOLID β review codebase real, identifikasi violation
L5: Design patterns (GoF) β pola per masalah
L6: Architecture (DDD, hexagonal) β boundary di level sistemCross-Link
- Paradigma β hierarchy-software-engineering-paradigm
- Design Patterns β design-patterns-gof
- Clean Code β clean-code-robert-martin, the-pragmatic-programmer
- Refactoring β refactoring-martin-fowler
- Master Index β master-index
OOP Deep Dive Β· Boundary > Alam Nyata Β· Encapsulation = Invariant Β· Inheritance = Hati-Hati Β· Composition First Β· SOLID = Konteks Β· Hybrid = Realita Β· YAGNI = Abstraksi