# EncapsulationA intermediate-level guide to Encapsulation: before-and-after java code and diagrams for a CS student.

## The Chaos of Unprotected Data

Imagine you are building a banking application. You have an object that holds a user's balance, and you want to make sure no one ever accidentally sets that balance to a negative number. However, if the variable holding that balance is visible to every other part of your program, any developer (or even another piece of code) could bypass your business logic and change the value directly.

When an object's internal state can be modified from the outside without any oversight, you lose control over the integrity of your data. This leads to "corrupted state," where an object exists in a way that should be impossible according to your rules—like an account with negative money when your bank policy forbids it.

```java
class BankAccount {
    public double balance;

    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }
}
```

In the diagram below, notice how the `balance` field is marked as `public`. This means any external entity can reach directly into the `BankAccount` and overwrite its value at will.

```mermaid
classDiagram
    class BankAccount {
        +double balance
    }
```

## Defining Encapsulation

Encapsulation is the technique of bundling data (variables) and the methods that operate on that data into a single unit, known as a class, while restricting direct access to some of the object's components. 

Think of a bank teller window. You cannot walk behind the counter and grab money from the vault yourself; you must interact with the teller through the service window. The teller acts as an intermediary who verifies your identity and ensures your transaction is valid before anything changes in the vault. In programming, these "tellers" are public methods, and the "vault" is your private data.

To achieve this, we use **access modifiers**:
*   **`private`**: This keyword hides a field so it can only be accessed or changed by code within the same class.
*   **`public`**: This keyword makes a method available to any other class that has a reference to your object.

## The Mechanics: How to Enforce Boundaries

To implement encapsulation properly, we hide our variables using `private` and provide controlled access through `public` methods. These methods act as gatekeepers. When someone wants to change the state (e.g., adding money), they call a method like `deposit()`. Inside that method, we write logic to validate the request before actually updating the private variable.

```java
class BankAccount {
    private double balance;

    public BankAccount(double initialBalance) {
        if (initialBalance < 0) {
            throw new IllegalArgumentException("Initial balance cannot be negative");
        }
        this.balance = initialBalance;
    }

    public double getBalance() {
        return balance;
    }

    public void deposit(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Deposit amount must be positive");
        }
        balance += amount;
    }
}
```

In this improved version, the `balance` is protected. The only way to modify it is through the `deposit` method or the constructor, both of which perform validation checks. If an invalid amount is provided, the object refuses to change its state and instead throws an error.

```mermaid
sequenceDiagram
    participant User
    participant BA as BankAccount
    User ->> BA: deposit(amount)
    alt amount > 0
        BA ->> BA: balance += amount
        BA -->> User: void
    else amount <= 0
        BA -->> User: throw IllegalArgumentException
    end
```

## The Nuance: Beyond Getters and Setters

A common mistake is thinking that encapsulation simply means adding "getters" (methods to read a value) and "setters" (methods to update a value). If your setter looks like this: `public void setBalance(double balance) { this.balance = balance; }`, you have not actually implemented encapsulation effectively because you haven't added any validation rules. You have simply made a private variable public through a different name.

True encapsulation is about protecting the object's integrity. If your method accepts data that would break the business rules of your application, the encapsulation has failed its primary purpose: managing complexity and preventing invalid states.

## When to Use It, and When Not To

Encapsulation is an essential tool for managing complexity in large systems. By restricting how data is modified, you ensure that if a bug occurs regarding a variable's value, you only have one place to look: the methods inside that specific class. This prevents "side effects," where changing code in one part of a program unexpectedly breaks an unrelated part because they were both sharing direct access to the same variable.

However, encapsulation is not a replacement for security; it is a tool for code correctness and maintainability. Over-engineering every single variable with complex validation when the data is internal and trivial can sometimes lead to unnecessary boilerplate code. Use it primarily when your object has rules that must never be violated.

## Takeaways

- Encapsulation protects an object's integrity by controlling how its data is changed via `private` fields and `public` methods.
- Validation logic inside methods prevents an object from entering an invalid state.
- A "leaky abstraction" occurs when you provide access to internal data without the necessary safeguards or validation.

## Usage

**Direct access allows the object to enter an invalid state.**

```java
BankAccount account = new BankAccount(100.0);
account.balance = -500.0; // Corrupts state: balance is now -400.0
```

**Encapsulation ensures the object remains in a valid state.**

```java
BankAccount account = new BankAccount(100.0);
account.deposit(-50.0); // Throws IllegalArgumentException
```
