Encapsulation

Part of Object-Oriented Programming

What is Encapsulation?

Encapsulation is the bundling of data (attributes) and methods that operate on that data within a single unit (class), while restricting direct access to some components.

It's like a protective wrapper that prevents external code from directly accessing internal data, instead providing controlled access through public methods.

Real-World Analogy

Think of an ATM machine. You can't directly access the cash inside - you must use the interface (card slot, PIN pad, buttons). The ATM validates your request and controls how much you can withdraw. The internal mechanisms are hidden (encapsulated) from you.

Interactive Visualization

Encapsulation Demo: Bank Account

class BankAccount:
private__balance = $1000
# Public methods
+ deposit(amount)
+ withdraw(amount)
+ get_balance()
Try accessing the account:
Key Concept: Encapsulation hides internal data (like __balance) and provides controlled access through public methods, preventing invalid states.

Access Modifiers

Public

  • • Accessible from anywhere
  • • No restrictions
  • • Python: name
  • • Java/C++: public

Protected

  • • Class + subclasses
  • • Inherited classes can access
  • • Python: _name
  • • Java/C++: protected

Private

  • • Only within the class
  • • Most restrictive
  • • Python: __name
  • • Java/C++: private

Code Implementation

# Encapsulation in Python

class BankAccount:
    def __init__(self, owner, initial_balance=0):
        self.owner = owner           # Public attribute
        self._account_type = "Savings"  # Protected (convention)
        self.__balance = initial_balance  # Private (name mangling)
    
    # Public methods - the interface
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            return f"Deposited ${amount}. New balance: ${self.__balance}"
        return "Invalid amount"
    
    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            return f"Withdrew ${amount}. New balance: ${self.__balance}"
        return "Insufficient funds or invalid amount"
    
    def get_balance(self):  # Getter
        return self.__balance
    
    # Using @property decorator (Pythonic way)
    @property
    def balance(self):
        return self.__balance
    
    @balance.setter
    def balance(self, value):
        if value >= 0:
            self.__balance = value
        else:
            raise ValueError("Balance cannot be negative")

# Usage
account = BankAccount("Alice", 1000)

# Public access works
print(account.owner)       # Alice
print(account.deposit(500))  # Deposited $500. New balance: $1500

# Direct access to private fails
# print(account.__balance)  # AttributeError!

# Use public methods instead
print(account.get_balance())  # 1500
print(account.balance)        # 1500 (using property)

# Attempting to set invalid balance
# account.balance = -100  # ValueError: Balance cannot be negative

Benefits of Encapsulation

Data Protection

Prevents accidental or malicious modification of data. Balance can't be set to negative values if the setter validates it.

Flexibility

Internal implementation can change without affecting external code. You could change from storing cents to dollars internally.

Easier Debugging

If data becomes invalid, you know it happened through a setter - easier to find the bug than if any code could modify the data.

Clear Interface

Public methods define a clear API. Users of the class know exactly what operations are available and supported.

Common Mistakes

Making everything public

Exposing all attributes as public defeats the purpose of encapsulation. Think about what really needs to be accessible from outside.

Getters/setters without validation

If your setter just assigns the value without validation, you might as well make the attribute public. Add meaningful validation logic.

Returning mutable objects directly

If you return a list or object reference, the caller can modify it. Return a copy instead: return self.__items.copy()

Interview Tips

Q:"Why use getters and setters instead of public attributes?"
A:"Getters/setters provide controlled access: you can add validation in setters (e.g., age must be positive), add logging, compute derived values in getters, or change internal representation later without breaking the API."

Test Your Knowledge

Encapsulation Quiz

Question 1 of 6

What is encapsulation in OOP?