State

State is a design pattern that allows an object to modify its behavior whenever its internal state changes, make it appear as through the object itself changed its class.

TL;DR

The state pattern allows you to change the behavior of a class whenever the state changes.

Problem

If you write a media player that implements multiple states, such as playing, paused, and stopped you might try to write something such as:

if state == "playing":
	...
elif state == "paused":
	...
 
etc. etc.

While this might work in a very small application, it becomes very complicated when more states need to be added.

Solution

The solution is to use the State pattern, which suggests that each state class encapsulates the logic for that state, and the context simply delegates to the current state.

from abc import ABC, abstractmethod
 
# State Interface
class State(ABC):
    @abstractmethod
    def play(self, player):
        pass
 
    @abstractmethod
    def pause(self, player):
        pass
 
    @abstractmethod
    def stop(self, player):
        pass
 
# Concrete States
class PlayingState(State):
    def play(self, player):
        print("Already playing.")
 
    def pause(self, player):
        print("Pausing playback.")
        player.set_state(PausedState())
 
    def stop(self, player):
        print("Stopping playback.")
        player.set_state(StoppedState())
 
class PausedState(State):
    def play(self, player):
        print("Resuming playback.")
        player.set_state(PlayingState())
 
    def pause(self, player):
        print("Already paused.")
 
    def stop(self, player):
        print("Stopping playback from pause.")
        player.set_state(StoppedState())
 
class StoppedState(State):
    def play(self, player):
        print("Starting playback.")
        player.set_state(PlayingState())
 
    def pause(self, player):
        print("Cannot pause. Player is stopped.")
 
    def stop(self, player):
        print("Already stopped.")
 
# Context
class MediaPlayer:
    def __init__(self):
        self.state = StoppedState()
 
    def set_state(self, state: State):
        self.state = state
 
    def play(self):
        self.state.play(self)
 
    def pause(self):
        self.state.pause(self)
 
    def stop(self):
        self.state.stop(self)
 
# Usage
player = MediaPlayer()
 
player.play()   # Starting playback.
player.pause()  # Pausing playback.
player.play()   # Resuming playback.
player.stop()   # Stopping playback.
player.pause()  # Cannot pause. Player is stopped.