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

## The "If-Else" Explosion

Imagine you are building software for a vending machine. Initially, the logic is simple: if it is idle, take money; if it has goods, dispense them. But as requirements grow—adding "out of order" modes, maintenance states, or error handling—the code begins to buckle under its own weight.

```java
class VendingMachine {
    private int balance = 0;
    private String state = "IDLE"; // Possible: IDLE, HAS_GOODS, SOLD

    public void insertCoin(int amount) {
        if (state.equals("IDLE")) {
            balance += amount;
            System.out.println("Balance: " + balance);
            state = "HAS_GOODS";
        } else if (state.equals("HAS_GOODS")) {
            System.out.println("Already in use.");
        } else {
            System.out.println("Machine is out of order.");
        }
    }

    public void pressButton() {
        if (state.equals("HAS_GOODS")) {
            System.out.println("Dispensing item...");
            state = "IDLE";
            balance = 0;
        } else if (state.equals("IDLE")) {
            System.out.println("Insert coins first.");
        } else {
            System.out.println("Cannot press button now.");
        }
    }
}
```

This class suffers from "Switch Statements," a code smell where a single method uses conditional logic to decide how to act based on an internal variable like `state`. As you can see in the diagram below, the behavior of `insertCoin` and `pressButton` is trapped inside large blocks that must check the current status every single time they are called.

```mermaid
flowchart TD
    A[insertCoin] --> B{state == IDLE?}
    B -- yes --> C[update balance/state]
    B -- no --> D{state == HAS_GOODS?}
    D -- yes --> E[print error]
    D -- no --> F[print error]

    G[pressButton] --> H{state == HAS_GOODS?}
    H -- yes --> I[dispense/reset]
    H -- no --> J{state == IDLE?}
    J -- yes --> K[print error]
    J -- no --> L[print error]
```

### Defining the Pain

This approach creates "conditional complexity." The problem isn't just that the code is long; it is that the logic is fragile. If you want to add a new state—for example, `REPAIR_MODE`—you cannot simply add one piece of code. You must hunt through every single method in the `VendingMachine` class and add another `else-if` branch to ensure the machine behaves correctly in that new mode. 

This violates the Open/Closed Principle: your classes should be open for extension but closed for modification. Currently, every time you extend the machine's capabilities, you are forced to modify (and potentially break) existing, working code.

## The Refactor: Replace Conditional with Polymorphism

To fix this, we use a refactoring technique called **Replace Conditional with Polymorphism**. Instead of asking "What state am I in?" inside every method, we delegate the work to an object that *represents* that state.

In this design, the `VendingMachine` (the Context) holds a reference to a `State` interface. The specific behavior is moved into separate classes like `IdleState` and `HasGoodsState`. When you call `insertCoin` on the machine, it simply says: "Hey, current state object, you handle this."

```java
interface State {
    void insertCoin(VendingMachine context, int amount);
    void pressButton(VendingMachine context);
}

class IdleState implements State {
    public void insertCoin(VendingMachine context, int amount) {
        context.setBalance(amount);
        System.out.println("Balance: " + amount);
        context.setState(new HasGoodsState());
    }
    public void pressButton(VendingMachine context) {
        System.out.println("Insert coins first.");
    }
}

class HasGoodsState implements State {
    public void insertCoin(VendingMachine context, int amount) {
        System.out.println("Already in use.");
    }
    public void pressButton(VendingMachine context) {
        System.out.println("Dispensing item...");
        context.setBalance(0);
        context.setState(new IdleState());
    }
}

class VendingMachine {
    private int balance = 0;
    private State state = new IdleState();

    public void insertCoin(int amount) {
        state.insertCoin(this, amount);
    }

    public void pressButton() {
        state.pressButton(this);
    }

    // Package-private for State implementation access
    void setState(State state) { this.state = state; }
    int getBalance() {
        return balance;
    }
    void setBalance(int balance) {
        this.balance = balance;
    }
}
```

By moving the logic into dedicated state classes, we achieve **encapsulation**. The `VendingMachine` no longer needs to know the rules for every possible scenario; it only needs to know how to talk to the `State` interface. 

The following class diagram shows how the machine delegates its responsibilities to these specialized objects:

```mermaid
classDiagram
    class VendingMachine {
        -int balance
        -State state
        +insertCoin(int amount)
        +pressButton()
        void setState(State state)
        int getBalance()
        void setBalance(int balance)
    }
    class State {
        <<interface>>
        +insertCoin(VendingMachine context, int amount)
        +pressButton(VendingMachine context)
    }
    class IdleState {
        +insertCoin(VendingMachine context, int amount)
        +pressButton(VendingMachine context)
    }
    class HasGoodsState {
        +insertCoin(VendingMachine context, int amount)
        +pressButton(VendingMachine context)
    }

    VendingMachine --> State : uses
    IdleState ..|> State : implements
    HasGoodsState ..|> State : implements
```

### How the Handshake Works

The magic happens during runtime. When a user interacts with the machine, a "handshake" occurs where the machine passes itself as an argument to the state object. This allows the state object to trigger transitions—like telling the machine to switch from `IdleState` to `HasGoodsState`.

```mermaid
sequenceDiagram
    participant VM as VendingMachine
    participant IS as IdleState
    participant HGS as HasGoodsState

    Note over VM, HGS: User inserts a coin
    VM ->> IS: insertCoin(this, amount)
    IS ->> VM: setBalance(amount)
    IS ->> VM: setState(new HasGoodsState())
    Note right of VM: The machine has now changed behavior!
```

This implementation is essentially a code-level version of a **Finite State Machine (FSM)**. Each class represents a discrete node in the machine's lifecycle, and the methods define the transitions between those nodes.

## When to Use It, and When Not To

The State pattern is powerful, but it comes with a cost: "class explosion." Because every new state requires a brand-new class file, you can end up with dozens of tiny classes that only exist to hold a few lines of logic.

**Use the State pattern when:**
*   An object's behavior depends on its current status AND is complex enough that `if/else` blocks are becoming unreadable.
*   You find yourself copying and pasting similar conditional checks across multiple methods in the same class.
*   Transitions between states involve significant logic or side effects.

**Avoid the State pattern when:**
*   The object only has two possible states (e.g., `ON` and `OFF`) that rarely change. A simple boolean flag is much more efficient and readable there.
*   You are working in a context where creating many small classes adds unnecessary overhead or complexity to the build process without providing clear benefits in readability.

## Takeaways

- Replace messy `if/else` chains with polymorphic calls to specialized state objects.
- Use this pattern when an object's behavior changes significantly based on its internal status.
- Refactoring to State makes your code easier to extend because adding a new state only requires adding a new class, not modifying existing logic in the main class.

## Usage

**Using the refactored VendingMachine context**

```java
VendingMachine vm = new VendingMachine();
vm.insertCoin(50);
vm.pressButton();
```
