🧩 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

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 dalam

Kunci: 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

SkenarioPilihKenapa
Subclass memang subtype (Cat β†’ Animal) + behavior extendsInheritanceIS-A valid, template method
Cuma mau pakai ulang methodCompositionDecouple, testable
Behavior berubah runtimeCompositionStrategy pattern
Deep hierarchy (>3 level)CompositionFragile base class
Framework callback (Android Activity, React component)Inheritance (forced)Framework contract

4. SOLID β€” Lima Prinsip yang (Sering) Disalahpahami

PrinsipNamaIntiSering Disalahpahami Sebagai
SSingle Responsibility1 class = 1 alasan berubah”1 class = 1 fungsi” ❌
OOpen/ClosedExtend tanpa modify”Harus pakai inheritance” ❌
LLiskov SubstitutionSubclass substitutable”Subclass harus punya semua method” ❌
IInterface SegregationClient jangan dipaksa depend ke method yang gak dipakai”1 method per interface” ❌
DDependency InversionDepend 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

AspekOOPFunctional
StateMutable, encapsulatedImmutable, explicit
UnitObject (state+behavior)Function (pure)
Data flowMessage passingPipeline / composition
Side effectsDikendalikan per classDikarantina (IO monad)
ConcurrencyShared state + lockImmutable β†’ lock-free
TestingMock dependencyPure function = trivial
BahasaJava, C++, Python, RubyHaskell, 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-PatternGejalaFix
God Object1 class ngurus segalanyaSRP β†’ pecah
Bean ClassClass cuma getter/setter tanpa behaviorDomain logic masuk ke class
Deep Hierarchy5+ level inheritanceComposition
Circle DependencyA→B→AInterface + DI container
Anemic Domain ModelEntity tanpa method, logic di serviceDomain model enrichment
Premature AbstractionInterface 1 implementasiYAGNI β€” tambah interface saat butuh 2 impl
Sequential CouplingMethod harus dipanggil urutan tertentuBuilder 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 cukup

9. 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 sistem


OOP Deep Dive Β· Boundary > Alam Nyata Β· Encapsulation = Invariant Β· Inheritance = Hati-Hati Β· Composition First Β· SOLID = Konteks Β· Hybrid = Realita Β· YAGNI = Abstraksi