A behavioural design pattern that allows an object to alter its behaviour when its internal state changes. The pattern represents each possible state of an object as a separate class and allows the object to delegate its state-dependent behaviour to these classes.
Structure:
Context: Defines the interface to interact with the state objects. It maintains an instance of a concrete state class that represents the current state of the context.
State: Declares an interface for encapsulating the behaviour associated with a particular state of the context.
Concrete States: Implement the behaviour associated with a specific state of the context. Each concrete state class handles operations differently based on the context's current state.
Explanation:
The Context class is the class whose behaviour varies based on its internal state. It maintains a reference to the current state object and delegates state-specific behaviour to this object.
The State interface defines methods that encapsulate the behaviour associated with each state of the context. It allows different concrete state classes to provide their implementations of these methods.
Each Concrete State class provides its implementation of the state-specific behaviour defined by the State interface. It represents a specific state of the context and handles operations differently based on this state.
Example:
Consider a vending machine that dispenses different items (e.g., snacks or beverages) based on its current state (e.g., whether it has sufficient change or the selected item is available). We can use the State pattern to represent the different states of the vending machine and delegate state-specific behaviour to concrete state classes:
// State protocol
protocol VendingMachineState {
func insertCoin(amount: Int)
func selectItem(item: String)
func dispenseItem()
}
// Concrete states
class ReadyState: VendingMachineState {
func insertCoin(amount: Int) {
print("Coin inserted: \(amount)")
}
func selectItem(item: String) {
print("Item selected: \(item)")
}
func dispenseItem() {
print("Please select an item")
}
}
class DispensingState: VendingMachineState {
func insertCoin(amount: Int) {
print("Coin inserted: \(amount)")
}
func selectItem(item: String) {
print("Item selected: \(item)")
}
func dispenseItem() {
print("Dispensing item...")
}
}
// Context
class VendingMachine {
private var state: VendingMachineState
init() {
self.state = ReadyState()
}
func setState(state: VendingMachineState) {
self.state = state
}
func insertCoin(amount: Int) {
state.insertCoin(amount: amount)
}
func selectItem(item: String) {
state.selectItem(item: item)
}
func dispenseItem() {
state.dispenseItem()
}
}
// Example usage
let vendingMachine = VendingMachine()
vendingMachine.insertCoin(amount: 10) // Output: Coin inserted: 10
vendingMachine.selectItem(item: "Chips") // Output: Item selected: Chips
vendingMachine.dispenseItem() // Output: Dispensing item...
In this example:
We have a VendingMachineState protocol defining methods for each state-specific behaviour.
Concrete state classes like ReadyState and DispensingState implement the VendingMachineState protocol with specific behaviours for different states.
The VendingMachine class represents the context whose behaviour varies based on its internal state. It maintains a reference to the current state object and delegates state-specific behaviour to this object.
Clients interact with the VendingMachine object, and its behaviour changes dynamically based on its current state.
Usage
Some common scenarios where the State pattern can be applied include:
User Interface (UI) Development: In graphical user interfaces (GUIs), the State pattern can be used to manage the behaviour of UI elements based on user interactions and system events. For example, the state of a button (e.g., enabled, disabled, clicked) can be represented by different state objects, each handling the button's behaviour accordingly.
Finite State Machines (FSMs): The State pattern is frequently used in implementing finite state machines, where an object transitions between a finite number of states in response to events or inputs. Each state is represented by a separate state class, and transitions between states are handled by the context object based on its current state.
Game Development: In game development, the State pattern can be used to manage the behaviour of game characters, game levels, or game modes based on various game events and player actions. For instance, a character's behaviour (e.g., idle, walking, attacking) can be implemented using different state classes.
Networking and Communication Protocols: Communication protocols, such as TCP/IP, often involve complex state transitions based on the network conditions and protocol specifications. The State pattern can be used to model these state transitions and handle protocol-specific behaviours at each state.
Workflow Management Systems: Workflow management systems often involve complex business processes with multiple states and transitions. The State pattern can be used to model the different states of a workflow and define the actions or operations that can be performed in each state.
Embedded Systems and Control Systems: In embedded systems and control systems, objects often exhibit different behaviours based on environmental conditions or sensor inputs. The State pattern can be used to model these behaviours and switch between different control strategies or algorithms dynamically.
Summary
Overall, the State pattern is beneficial in scenarios where an object's behaviour varies based on its internal state, and the behaviour needs to be encapsulated and managed independently of the object's context. It promotes flexibility, maintainability, and extensibility by separating the behaviour into distinct state classes and allowing for easy addition or modification of states.
Top comments (0)