State Design Pattern in Kotlin

1. Definition

The State Design Pattern allows an object to change its behavior when its internal state changes. This can be done in such a way that it appears as if the object has changed its class.

2. Problem Statement

Imagine an object that has different behaviors based on its current state. Directly managing these state-dependent behaviors within the object can make the code complex, hard to read, and maintain.

3. Solution

The State pattern suggests that you create new classes for all possible states of an object and extract all state-specific behaviors into these classes. The original object delegates the work to a representative instance of one of these states.

4. Real-World Use Cases

1. A vending machine which has different states like 'idle', 'waiting for payment', 'dispensing item'.

2. A media player that can be in states like 'playing', 'paused', 'stopped'.

5. Implementation Steps

1. Create an interface to represent different states.

2. Implement concrete classes for each state based on the state interface.

3. The context class that maintains a reference to the current state and can transition between states.

6. Implementation in Kotlin Programming

// Step 1: State interface
interface State {
    fun handle(context: MediaPlayer)
}
// Step 2: Concrete States
class PlayingState : State {
    override fun handle(context: MediaPlayer) {
        println("Pausing media player.")
        context.state = PausedState()
    }
}
class PausedState : State {
    override fun handle(context: MediaPlayer) {
        println("Resuming media player.")
        context.state = PlayingState()
    }
}
class StoppedState : State {
    override fun handle(context: MediaPlayer) {
        println("Starting media player.")
        context.state = PlayingState()
    }
}
// Step 3: Context Class
class MediaPlayer {
    var state: State = StoppedState()
    fun playPauseButtonPressed() {
        state.handle(this)
    }
}
// Client Code
fun main() {
    val player = MediaPlayer()
    player.playPauseButtonPressed()  // Starts the player
    player.playPauseButtonPressed()  // Pauses the player
    player.playPauseButtonPressed()  // Resumes the player
}

Output:

Starting media player.
Pausing media player.
Resuming media player.

Explanation:

1. The State interface provides a contract for all states.

2. Concrete states (PlayingState, PausedState, StoppedState) define behaviors associated with specific states.

3. MediaPlayer, the context, maintains a reference to its current state and can transition between states when the 'playPauseButtonPressed' method is invoked.

7. When to use?

Use the State pattern when:

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

2. Code has many conditional statements over an object's state and is organized around the state.

3. Transitions between states, as well as state-specific behaviors, need to be defined clearly and decoupled from the main object.

Comments