OOP Fundamentals • EncapsulationHard⏱️ ~3 min
Trade-offs: When to Use Encapsulation
Decision Framework: Encapsulation is not free. It adds indirection and requires careful interface design. Understanding trade-offs helps make informed decisions.
When Encapsulation is Essential:
First, Complex Invariants: When multiple fields must stay synchronized (e.g.,
ParkingLot.capacity and occupiedCount), encapsulation prevents inconsistency.Second, Validation Requirements: Business rules like "balance cannot be negative" or "age must be 0-120" require controlled access through methods.
Third, Future Flexibility: When internal representation might change (e.g., switching from array to HashMap for spots), encapsulation isolates clients from implementation.
Fourth, Security/Safety: Sensitive data like passwords, payment info, or permissions must be hidden and accessed through validated interfaces.
When Encapsulation is Overkill:
First, DTOs (Data Transfer Objects): Pure data containers like
UserDTO or API request/response models have no invariants. Public fields or simple getters/setters suffice.Second, Immutable Value Objects: Once created, fields never change. A
Point(x, y) or Money(amount, currency) can expose fields as public final/readonly.Third, Internal Helper Classes: Private nested classes used only within another class don't need encapsulation from themselves.
Fourth, Performance-Critical Code: In rare cases (game engines, numerical computing), direct field access avoids method call overhead. Profile first.
Encapsulated (Right)
class BankAccount {
- balance: Money
+ withdraw(amt): Result
}
- balance: Money
+ withdraw(amt): Result
}
Has invariants (balance ≥ 0), validation needed
Public Fields (Right)
class Point {
+ readonly x: int
+ readonly y: int
}
+ readonly x: int
+ readonly y: int
}
Immutable, no invariants, simple data
Common Anti-Pattern: Anemic Getters/Setters
❌ False Encapsulation
class Account {
- balance: Money
+ getBalance(): Money { return balance }
+ setBalance(b: Money) { balance = b }
}
- balance: Money
+ getBalance(): Money { return balance }
+ setBalance(b: Money) { balance = b }
}
Fields are private but setter allows any value. No validation. This is not true encapsulation—just indirection without protection.
✓ True Encapsulation
class Account {
- balance: Money
+ deposit(amt: Money): void
+ withdraw(amt: Money): Result
+ getBalance(): Money
}
- balance: Money
+ deposit(amt: Money): void
+ withdraw(amt: Money): Result
+ getBalance(): Money
}
No setter. Behavior-focused methods that enforce rules. Balance can only change through validated operations.
Interview Tip: If asked "Should all fields be private with getters/setters?", answer "No". Explain DTOs vs domain objects. Show you understand the difference between mechanical encapsulation and protecting invariants.
💡 Key Takeaways
✓Encapsulation is essential when invariants must be protected
✓DTOs (Data Transfer Objects) don't need encapsulation
✓Getters/setters without validation are false encapsulation
✓Immutable value objects can safely expose fields
✓Trade verbosity for safety only when safety matters
📌 Examples
1BankAccount needs encapsulation, UserDTO does not
2Point as immutable value object with public fields
3ParkingSpot needs encapsulation to prevent double-booking