State Design Pattern in Swift

1. Definition

The State pattern is a behavioral design pattern that allows an object to change its behavior when its internal state changes, giving the impression that the object changed its class.

2. Problem Statement

Consider a vending machine. The machine has several states like idle, awaiting payment, dispensing item, and out of stock. It would be inefficient and cluttered to put all the state-related behaviors in the main VendingMachine class.

3. Solution

The State pattern suggests that you create individual state classes for each state of an object. The main object delegates the state-related behavior to the current state object instead of implementing state behaviors on its own.

4. Real-World Use Cases

1. Vending Machines with different states like idle, accepting payment, dispensing, etc.

2. A music player that can be in states like playing, paused, or stopped.

3. Traffic lights transitioning between green, yellow, and red.

5. Implementation Steps

1. Define a State protocol that declares methods to handle requests, which will be implemented differently for each state.

2. Implement concrete classes for each state based on the State protocol.

3. The main class (context) maintains a reference to the current state and delegates the state-specific behavior to that object.

6. Implementation in Swift Programming

// State protocol
protocol State {
    func insertCoin()
    func ejectCoin()
    func dispense()
}
// Concrete States
class IdleState: State {
    func insertCoin() {
        print("Coin accepted. Choose your item.")
    }
    func ejectCoin() {
        print("No coin to eject.")
    }
    func dispense() {
        print("Please insert a coin first.")
    }
}
class HasCoinState: State {
    func insertCoin() {
        print("You can't insert another coin.")
    }
    func ejectCoin() {
        print("Coin returned.")
    }
    func dispense() {
        print("Dispensing item...")
    }
}
// Vending Machine (Context)
class VendingMachine {
    private var currentState: State = IdleState()
    func setState(_ state: State) {
        self.currentState = state
    }
    func insertCoin() {
        currentState.insertCoin()
    }
    func ejectCoin() {
        currentState.ejectCoin()
    }
    func dispense() {
        currentState.dispense()
    }
}
// Usage
let machine = VendingMachine()
machine.insertCoin()
machine.dispense()

Output:

Coin accepted. Choose your item.
Please insert a coin first.

Explanation:

1. The State protocol represents different states with their behaviors.

2. Concrete state classes (IdleState and HasCoinState) provide implementations for the behaviors defined in the State protocol.

3. The VendingMachine class, which is the context, delegates state-specific behavior to the current state object.

4. Instead of the VendingMachine class having a lot of conditional logic to determine the behavior based on the state, the behavior gets nicely separated into different state classes.

7. When to use?

Use the State pattern when:

1. An object's behavior depends on its state, and it must change its behavior at runtime based on that state.

2. Operations have large, multi-part conditional statements that depend on the object's state.


Comments