State
The State pattern allows an object to alter its behavior when its internal state changes. This pattern avoids the use of large conditional statements (like if/else or switch) that often become unwieldy and difficult to maintain when dealing with complex state-dependent logic. Instead, each state is represented by a separate class, leading to a more organized and extensible design.
Essentially, the pattern encapsulates the different states of an object, along with the transitions between those states, into classes. The context object, which represents the object whose behavior changes, delegates the requests to the current state object. This enables the object to seamlessly switch between behaviors based on its state.
Usage
The State pattern is commonly used in scenarios where an object’s behavior is dictated by its state and needs to change dynamically. Some typical use cases include:
- User Interface (UI): Implementing different states for UI elements like buttons (enabled, disabled, hovered, pressed).
- Game Development: Defining different states for game characters (idle, walking, running, jumping, attacking).
- Workflow Management: Modeling different stages in a process, such as order processing (pending, processing, shipped, delivered).
- Communication Protocols: Representing the different phases of a network connection (listening, connecting, connected, closing).
- State Machines: More generally, implementing complex state machines where the object transitions between a well-defined set of states based on external events.
Examples
-
TCP Connection: A TCP connection goes through various states (SYN_SENT, ESTABLISHED, FIN_WAIT_1, etc.). Each state handles incoming and outgoing data differently. The TCP protocol itself effectively uses a state machine implemented with concepts similar to the State pattern.
-
Java’s
java.util.concurrent.locks.Lockinterface & implementations: Specifically, the state management behind acquiring and releasing a lock relies on a pattern similar to State. A lock might be in an ‘unlocked’ state, transition to a ‘locked’ state when acquired, and allow releases only when locked. The internal implementation will handle the state transitions and ensure thread safety. -
Android Activity Lifecycle: An Android Activity’s lifecycle consists of states like
CREATED,STARTED,RESUMED,PAUSED,STOPPED, andDESTROYED. Each state dictates what the activity can and cannot do, and the Android framework internally manages these state transitions.
Specimens
15 implementationsThe State pattern allows an object to alter its behavior when its internal state changes. It encapsulates each state into separate classes, avoiding the use of conditional statements based on the object’s state. Each state class handles requests differently.
This Dart implementation demonstrates a simple traffic light with three states: Red, Yellow, and Green. The TrafficLight class maintains a currentState object. The setState method allows transitioning between states. Each state (RedState, YellowState, GreenState) implements the TrafficLightState interface, defining how the light handles a next() call, resulting in state changes and appropriate actions (printing the current light). Using interfaces promotes loose coupling and extensibility. This approach utilizes classes and interfaces, aligning with Dart’s strong support for OOP and its emphasis on defining clear contracts through interfaces.
// Define the State interface
abstract class TrafficLightState {
void next(TrafficLight trafficLight);
}
// Concrete State classes
class RedState implements TrafficLightState {
@override
void next(TrafficLight trafficLight) {
print('Red -> Yellow');
trafficLight.setState(YellowState());
}
}
class YellowState implements TrafficLightState {
@override
void next(TrafficLight trafficLight) {
print('Yellow -> Green');
trafficLight.setState(GreenState());
}
}
class GreenState implements TrafficLightState {
@override
void next(TrafficLight trafficLight) {
print('Green -> Red');
trafficLight.setState(RedState());
}
}
// Context class
class TrafficLight {
TrafficLightState currentState = RedState();
void setState(TrafficLightState state) {
currentState = state;
}
void next() {
currentState.next(this);
}
String getCurrentState() {
if (currentState is RedState) {
return 'Red';
} else if (currentState is YellowState) {
return 'Yellow';
} else {
return 'Green';
}
}
}
// Example Usage
void main() {
TrafficLight trafficLight = TrafficLight();
print('Initial State: ${trafficLight.getCurrentState()}');
trafficLight.next(); // Red -> Yellow
print('Current State: ${trafficLight.getCurrentState()}');
trafficLight.next(); // Yellow -> Green
print('Current State: ${trafficLight.getCurrentState()}');
trafficLight.next(); // Green -> Red
print('Current State: ${trafficLight.getCurrentState()}');
}
The State pattern allows an object to alter its behavior when its internal state changes. Instead of implementing state-dependent logic with conditional statements (if/else or switch/case), it encapsulates each state into a separate class. These state classes define the behavior for that specific state, and a context object holds a reference to the current state, delegating requests to it. This promotes single-responsibility principle and makes extending with new states easier without modifying existing code.
This Scala example implements a simple traffic light with three states: Red, Yellow, and Green. The TrafficLight class represents the context and holds the current state. The State trait defines the nextState method, allowing each concrete state to transition to the next. The implementation leverages Scala’s case classes for concise state definitions and pattern matching for clean state transitions, fitting the language’s functional style.
sealed trait State {
def nextState: State
def getColor: String
}
case object Red extends State {
def nextState: State = Green
def getColor: String = "Red"
}
case object Yellow extends State {
def nextState: State = Red
def getColor: String = "Yellow"
}
case object Green extends State {
def nextState: State = Yellow
def getColor: String = "Green"
}
class TrafficLight(initialState: State) {
private var currentState: State = initialState
def getColor: String = currentState.getColor
def next: Unit = {
currentState = currentState.nextState
}
}
object TrafficLightExample {
def main(args: Array[String]): Unit = {
val light = new TrafficLight(Red)
println(s"Initial color: ${light.getColor}")
light.next()
println(s"Next color: ${light.getColor}")
light.next()
println(s"Next color: ${light.getColor}")
light.next()
println(s"Next color: ${light.getColor}")
}
}
The State pattern allows an object to alter its behavior when its internal state changes. It encapsulates each state as a separate class, making it easy to add new states without modifying the context object. This promotes the Open/Closed Principle.
The code defines a TrafficLight context and states (Red, Yellow, Green). Each state implements the LightState interface, providing a handle() method to define the behavior for that state. The TrafficLight holds a reference to the current LightState and delegates the task of handling the light to it. State transitions are managed within each state’s handle() method. This aligns with PHP’s object-oriented principles and demonstrates a clean separation of concerns, using interfaces and classes for a structured approach.
<?php
interface LightState {
public function handle(TrafficLight $trafficLight);
}
class Red implements LightState {
public function handle(TrafficLight $trafficLight) {
echo "Red Light: Stop!\n";
$trafficLight->setState(new Yellow());
}
}
class Yellow implements LightState {
public function handle(TrafficLight $trafficLight) {
echo "Yellow Light: Caution!\n";
$trafficLight->setState(new Green());
}
}
class Green implements LightState {
public function handle(TrafficLight $trafficLight) {
echo "Green Light: Go!\n";
$trafficLight->setState(new Red());
}
}
class TrafficLight {
private LightState $state;
public function __construct() {
$this->setState(new Red());
}
public function setState(LightState $state): void {
$this->state = $state;
}
public function handle(): void {
$this->state->handle($this);
}
}
// Usage
$trafficLight = new TrafficLight();
for ($i = 0; $i < 5; $i++) {
$trafficLight->handle();
}
?>
The State pattern is a behavioral design pattern that lets an object alter its behavior when its internal state changes. It avoids using many conditional statements based on the object’s state by encapsulating each state into separate classes. The context object delegates the work to a state object, which determines how to handle the request.
This Ruby implementation defines a Context (Traffic Light) and several State classes (Red, Yellow, Green). Each state class implements a handle method which defines the behavior for that state. The Context holds an instance of the current State and delegates the action (stopping or going) to it. Using classes for each state promotes maintainability and avoids large, complex conditional blocks. This is very Ruby-like, leveraging the language’s class-based structure for clear separation of concerns and polymorphism.
# frozen_string_literal: true
# Define the State interface
module State
def handle
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
# Concrete State classes
class Red
include State
def handle
puts "Traffic light is RED. STOP!"
end
end
class Yellow
include State
def handle
puts "Traffic light is YELLOW. Prepare to stop!"
end
end
class Green
include State
def handle
puts "Traffic light is GREEN. GO!"
end
end
# The Context
class TrafficLight
attr_accessor :current_state
def initialize
@current_state = Red.new # Initial state
end
def change_state(state)
@current_state = state
end
def drive
@current_state.handle
end
end
# Usage
traffic_light = TrafficLight.new
traffic_light.drive # Output: Traffic light is RED. STOP!
traffic_light.change_state(Green.new)
traffic_light.drive # Output: Traffic light is GREEN. GO!
traffic_light.change_state(Yellow.new)
traffic_light.drive # Output: Traffic light is YELLOW. Prepare to stop!
The State pattern is a behavioral pattern that allows an object to alter its behavior when its internal state changes. Instead of implementing state-specific logic within a single class using conditional statements, the pattern creates separate state classes to encapsulate each behavior. A context class holds a reference to the current state and delegates requests to it, thus changing the object’s behavior dynamically.
This Swift implementation defines a TrafficLight context and separate State protocols conforming types for Red, Yellow, and Green. The TrafficLight maintains a current state and the nextState() method handles transitions. Each state dictates available actions (e.g., showLight for what color is displayed). This approach utilizes protocols and enums—core Swift features—for type safety and clear state representation, fitting the language’s emphasis on these constructs.
enum TrafficLightState: State {
case red
case yellow
case green
func showLight() -> String {
switch self {
case .red: return "Red"
case .yellow: return "Yellow"
case .green: return "Green"
}
}
func nextState() -> TrafficLightState {
switch self {
case .red: return .green
case .yellow: return .red
case .green: return .yellow
}
}
}
protocol State {
func showLight() -> String
func nextState() -> TrafficLightState
}
class TrafficLight {
private var currentState: TrafficLightState = .red
func showCurrentLight() -> String {
return currentState.showLight()
}
func changeState() {
currentState = currentState.nextState()
}
}
// Example Usage
let trafficLight = TrafficLight()
print(trafficLight.showCurrentLight()) // Red
trafficLight.changeState()
print(trafficLight.showCurrentLight()) // Green
trafficLight.changeState()
print(trafficLight.showCurrentLight()) // Yellow
trafficLight.changeState()
print(trafficLight.showCurrentLight()) // Red
The State pattern allows an object to alter its behavior when its internal state changes. It encapsulates each state as a separate class and provides a way to transition between them. This avoids complex conditional logic and makes the code more maintainable and extensible by adding new states without modifying the original context class.
Here, the TrafficLight is the context, and RedState, YellowState, and GreenState represent the possible states. Each state handles the display() and nextState() logic specific to that state. The state transition is managed within the TrafficLight class, delegating behavior to the current state object. This implementation leverages Kotlin’s class structure and object-oriented approach, utilizing interfaces for state definition and clear, concise function implementations.
// Define the State interface
interface State {
fun display()
fun nextState()
}
// Concrete states
class RedState : State {
override fun display() {
println("RED")
}
override fun nextState() {
println("Red -> Green")
}
}
class YellowState : State {
override fun display() {
println("YELLOW")
}
override fun nextState() {
println("Yellow -> Red")
}
}
class GreenState : State {
override fun display() {
println("GREEN")
}
override fun nextState() {
println("Green -> Yellow")
}
}
// Context class
class TrafficLight(private var state: State = RedState()) {
fun setState(state: State) {
this.state = state
}
fun display() {
state.display()
}
fun nextState() {
state.nextState()
when (state) {
is RedState -> setState(GreenState())
is GreenState -> setState(YellowState())
is YellowState -> setState(RedState())
}
}
}
fun main() {
val trafficLight = TrafficLight()
trafficLight.display()
trafficLight.nextState()
trafficLight.display()
trafficLight.nextState()
trafficLight.display()
trafficLight.nextState()
trafficLight.display()
}
The State pattern allows an object to alter its behavior when its internal state changes. It encapsulates different states into separate classes and provides a way to transition between them. This avoids using numerous conditional statements to handle state-specific logic.
The Rust code defines an enum State representing the possible states of a context. A Context struct holds the current state and delegates behavior to it. Each state is implemented as a trait object, allowing dynamic state switching. The code exemplifies simple state transitions and demonstrates how context behavior depends upon the currently active state. This approach leverages Rust’s ownership and borrowing system, along with trait objects for polymorphism, making it both safe and idiomatic for managing complex stateful behavior.
trait State {
fn handle(&self, context: &mut Context);
}
struct ConcreteStateA;
impl State for ConcreteStateA {
fn handle(&self, context: &mut Context) {
println!("State A handling request.");
context.set_state(Box::new(ConcreteStateB));
}
}
struct ConcreteStateB;
impl State for ConcreteStateB {
fn handle(&self, context: &mut Context) {
println!("State B handling request.");
context.set_state(Box::new(ConcreteStateA));
}
}
struct Context {
state: Box<dyn State>,
}
impl Context {
fn new() -> Self {
Context {
state: Box::new(ConcreteStateA),
}
}
fn set_state(&mut self, state: Box<dyn State>) {
self.state = state;
}
fn handle_request(&mut self) {
self.state.handle(self);
}
}
fn main() {
let mut context = Context::new();
context.handle_request();
context.handle_request();
context.handle_request();
}
The State pattern is a behavioral pattern that allows an object to alter its behavior when its internal state changes. It encapsulates different states of an object as separate classes (concrete states) and provides a way to transition between them. This avoids complex conditional logic within the object itself.
The Go implementation defines a State interface representing possible states. Concrete state structs (Red, Yellow, Green) implement this interface, each with its own behavior for handling a state transition (e.g., Process). A TrafficLight struct holds the current state and a method Transition to change it. This is idiomatic Go because it leverages interfaces for polymorphism, struct composition for behavior, and avoids direct modification of the TrafficLight’s state from outside, favoring managed transitions.
// traffic_light.go
package main
import "fmt"
// State interface defines the behavior of each state.
type State interface {
Process(light *TrafficLight)
}
// Red state.
type Red struct{}
func (r *Red) Process(light *TrafficLight) {
fmt.Println("Red Light: Stop!")
light.setState(&Yellow{}) // Transition to Yellow
}
// Yellow state.
type Yellow struct{}
func (y *Yellow) Process(light *TrafficLight) {
fmt.Println("Yellow Light: Caution!")
light.setState(&Green{}) // Transition to Green
}
// Green state.
type Green struct{}
func (g *Green) Process(light *TrafficLight) {
fmt.Println("Green Light: Go!")
light.setState(&Red{}) // Transition to Red
}
// TrafficLight context.
type TrafficLight struct {
currentState State
}
// NewTrafficLight creates a new TrafficLight in the Red state.
func NewTrafficLight() *TrafficLight {
return &TrafficLight{currentState: &Red{}}
}
// Transition changes the current state.
func (light *TrafficLight) Transition() {
light.currentState.Process(light)
}
// setState sets the current state. Package-level access to maintain control.
func (light *TrafficLight) setState(state State) {
light.currentState = state
}
func main() {
light := NewTrafficLight()
for i := 0; i < 5; i++ {
light.Transition()
}
}
The State pattern is a behavioral design pattern that allows an object to alter its behavior when its internal state changes. It encapsulates each state as a separate class, making it easier to add new states and manage complex conditional logic. This implementation uses function pointers to represent the state of a traffic light. Each state (Red, Yellow, Green) has a corresponding function pointer that defines its behavior – what action to take when the ’tick’ method is called. This approach is common in C due to its limited support for OOP features like inheritance. The structure and use of function pointers align with C’s procedural style and efficient memory management.
#include <stdio.h>
// Define the Traffic Light States
typedef enum {
RED,
YELLOW,
GREEN
} TrafficLightState;
// Define the actions for each state
typedef void (*TrafficLightAction)(void);
// Declare the functions (forward declaration)
void redAction(void);
void yellowAction(void);
void greenAction(void);
// Traffic Light Structure
typedef struct {
TrafficLightState state;
TrafficLightAction action;
} TrafficLight;
// Initialize the Traffic Light
void trafficLightInit(TrafficLight *light, TrafficLightState initialState) {
light->state = initialState;
switch (initialState) {
case RED:
light->action = redAction;
break;
case YELLOW:
light->action = yellowAction;
break;
case GREEN:
light->action = greenAction;
break;
}
}
// 'Tick' the Traffic Light - transitions to next state.
void trafficLightTick(TrafficLight *light) {
light->action(); // Perform action for the current state
switch (light->state) {
case RED:
light->state = GREEN;
light->action = greenAction;
break;
case GREEN:
light->state = YELLOW;
light->action = yellowAction;
break;
case YELLOW:
light->state = RED;
light->action = redAction;
break;
}
}
// State Actions
void redAction(void) {
printf("Red Light: Stop!\n");
}
void yellowAction(void) {
printf("Yellow Light: Caution!\n");
}
void greenAction(void) {
printf("Green Light: Go!\n");
}
int main() {
TrafficLight light;
trafficLightInit(&light, RED);
for (int i = 0; i < 5; i++) {
trafficLightTick(&light);
printf("Current State: %d\n", light.state);
}
return 0;
}
The State pattern is a behavioral pattern that allows an object to alter its behavior when its internal state changes. It separates the state-dependent behavior from the object itself, encapsulating each state in a separate class. This promotes the Single Responsibility Principle and makes state transitions more manageable.
The code demonstrates a simple traffic light with states: Red, Yellow, and Green. Each state is represented by a derived class of the abstract TrafficLightState class, implementing the handle() method to define the light’s behavior. The TrafficLight class holds a pointer to the current state and delegates behavior to it. State transitions are handled within the states themselves, changing the TrafficLight’s current state as needed. This implementation is idiomatic C++ using inheritance and polymorphism.
#include <iostream>
#include <memory>
// Forward declaration
class TrafficLight;
// Abstract State class
class TrafficLightState {
public:
virtual void handle(TrafficLight& light) = 0;
virtual ~TrafficLightState() = default;
};
// Concrete State classes
class RedState : public TrafficLightState {
public:
void handle(TrafficLight& light) override {
std::cout << "RED: Stop!\n";
light.setState(std::make_unique<YellowState>());
}
};
class YellowState : public TrafficLightState {
public:
void handle(TrafficLight& light) override {
std::cout << "YELLOW: Caution!\n";
light.setState(std::make_unique<GreenState>());
}
};
class GreenState : public TrafficLightState {
public:
void handle(TrafficLight& light) override {
std::cout << "GREEN: Go!\n";
light.setState(std::make_unique<RedState>());
}
};
// Context class
class TrafficLight {
private:
std::unique_ptr<TrafficLightState> currentState;
public:
TrafficLight() : currentState(std::make_unique<RedState>()) {}
void setState(std::unique_ptr<TrafficLightState> state) {
currentState = std::move(state);
}
void handle() {
currentState->handle(*this);
}
};
int main() {
TrafficLight light;
for (int i = 0; i < 5; ++i) {
light.handle();
}
return 0;
}
The State pattern allows an object to alter its behavior when its internal state changes. It encapsulates each state as a separate class, avoiding large conditional statements and promoting the Open/Closed Principle.
This C# example models a simple traffic light. Each color (Red, Yellow, Green) is a separate state class implementing a common TrafficLightState interface. The TrafficLightContext manages the current state. Transitions between states are handled within the state classes themselves, making the context unaware of specific state logic. The use of interfaces and abstract classes promotes loose coupling and extensibility - new states can be added without modifying existing ones. This is a clean and standard approach to state management in C#.
// Define the State interface
public interface ITrafficLightState
{
void Handle(TrafficLightContext context);
}
// Concrete State: Red
public class RedState : ITrafficLightState
{
public void Handle(TrafficLightContext context)
{
Console.WriteLine("Traffic Light: RED - Stop!");
context.SetState(new YellowState()); // Transition to Yellow
}
}
// Concrete State: Yellow
public class YellowState : ITrafficLightState
{
public void Handle(TrafficLightContext context)
{
Console.WriteLine("Traffic Light: YELLOW - Caution!");
context.SetState(new GreenState()); // Transition to Green
}
}
// Concrete State: Green
public class GreenState : ITrafficLightState
{
public void Handle(TrafficLightContext context)
{
Console.WriteLine("Traffic Light: GREEN - Go!");
context.SetState(new RedState()); // Transition to Red
}
}
// The Context
public class TrafficLightContext
{
private ITrafficLightState _state;
public TrafficLightContext()
{
_state = new RedState(); // Initial state
}
public void SetState(ITrafficLightState state)
{
_state = state;
}
public void Request()
{
_state.Handle(this);
}
}
// Example Usage
public class Example
{
public static void Main(string[] args)
{
TrafficLightContext trafficLight = new TrafficLightContext();
for (int i = 0; i < 5; i++)
{
trafficLight.Request();
}
}
}
The State pattern allows an object to alter its behavior when its internal state changes. It encapsulates each state as a separate class, promoting single-responsibility and open/closed principles. Instead of using conditional logic based on a state variable, the current state object handles the specific requests.
This TypeScript implementation models a traffic light. Each light color (Red, Yellow, Green) is a State class. The TrafficLight class delegates behavior to the current State instance. Using classes for states and interfaces for state behavior is a natural fit for TypeScript’s OOP capabilities, ensuring type safety and clear contracts. It promotes maintainability, as adding new states only requires creating new classes without modifying existing ones.
// Define the State interface
interface TrafficLightState {
handle(): string;
}
// Concrete State: Red Light
class RedLight implements TrafficLightState {
public handle(): string {
return "Stop!";
}
}
// Concrete State: Yellow Light
class YellowLight implements TrafficLightState {
public handle(): string {
return "Caution!";
}
}
// Concrete State: Green Light
class GreenLight implements TrafficLightState {
public handle(): string {
return "Go!";
}
}
// The Context: Traffic Light
class TrafficLight {
private currentState: TrafficLightState;
constructor() {
this.currentState = new RedLight(); // Initial state
}
public setCurrentState(state: TrafficLightState): void {
this.currentState = state;
}
public handleRequest(): string {
return this.currentState.handle();
}
}
// Usage
const trafficLight = new TrafficLight();
console.log(trafficLight.handleRequest()); // Output: Stop!
trafficLight.setCurrentState(new GreenLight());
console.log(trafficLight.handleRequest()); // Output: Go!
trafficLight.setCurrentState(new YellowLight());
console.log(trafficLight.handleRequest()); // Output: Caution!
The State pattern allows an object to alter its behavior when its internal state changes. Instead of using conditional logic to handle different states, the pattern creates separate state classes to encapsulate each state’s behavior. This promotes the Single Responsibility Principle and makes the code more maintainable and extensible.
This JavaScript implementation models a traffic light using the State pattern. Each state (Red, Yellow, Green) is a class implementing a handle() method that defines the light’s action when triggered. A TrafficLight class manages the current state and delegates requests to it. Using classes for each state and composition within TrafficLight are idiomatic JavaScript for structuring state-based logic and avoiding long if/else chains.
// State interface
class State {
handle() {
throw new Error("Handle method must be implemented in subclasses");
}
}
// Concrete states
class Red extends State {
handle() {
console.log("Red Light: Stop!");
return new Yellow(); // Transition to the next state
}
}
class Yellow extends State {
handle() {
console.log("Yellow Light: Caution!");
return new Green(); // Transition to the next state
}
}
class Green extends State {
handle() {
console.log("Green Light: Go!");
return new Red(); // Transition to the next state
}
}
// Context class
class TrafficLight {
constructor() {
this.state = new Red();
}
setState(state) {
this.state = state;
}
handle() {
this.state.handle();
}
}
// Example usage:
const trafficLight = new TrafficLight();
for (let i = 0; i < 5; i++) {
trafficLight.handle();
}
The State pattern allows an object to alter its behavior when its internal state changes. This avoids using complex conditional logic (if/else or switch/case) by encapsulating each state as a separate class. The context object holds a reference to a current state object and delegates behavior to it. This example models a traffic light with states Red, Yellow, and Green. Each state class defines the handle() method representing the light’s action for a given signal. The TrafficLight class, the context, manages the current state and transitions between them. Using classes for each state is a natural fit for Python’s object-oriented capabilities and promotes readability.
# traffic_light.py
class State:
def handle(self):
raise NotImplementedError
class RedState(State):
def handle(self):
print("Red: Stop!")
class YellowState(State):
def handle(self):
print("Yellow: Caution, prepare to stop.")
class GreenState(State):
def handle(self):
print("Green: Go!")
class TrafficLight:
def __init__(self):
self._state = RedState()
def change_state(self, state):
self._state = state
def signal(self):
self._state.handle()
if __name__ == "__main__":
light = TrafficLight()
light.signal() # Red: Stop!
light.change_state(GreenState())
light.signal() # Green: Go!
light.change_state(YellowState())
light.signal() # Yellow: Caution, prepare to stop.
The State pattern allows an object to alter its behavior when its internal state changes. It encapsulates each state into separate classes, making the code more organized and easier to maintain. Instead of a complex series of if/else or switch statements to handle different states, the object delegates the behavior to its current state object.
This Java example models a traffic light. The TrafficLight class maintains a currentState (an instance of a State subclass) and delegates the handle() method to it. Concrete states (RedState, YellowState, GreenState) implement the State interface, defining the traffic light’s behavior in each respective state. Transitions between states are managed within each state’s handle() method. This approach follows Java’s object-oriented principles, especially encapsulation and polymorphism, and avoids tight coupling between state logic and the main TrafficLight class.
// State Interface
interface State {
void handle(TrafficLight trafficLight);
}
// Concrete States
class RedState implements State {
@Override
public void handle(TrafficLight trafficLight) {
System.out.println("Traffic light is RED. Stopping cars.");
trafficLight.changeState(new GreenState());
}
}
class YellowState implements State {
@Override
public void handle(TrafficLight trafficLight) {
System.out.println("Traffic light is YELLOW. Prepare to stop or go!");
trafficLight.changeState(new RedState());
}
}
class GreenState implements State {
@Override
public void handle(TrafficLight trafficLight) {
System.out.println("Traffic light is GREEN. Go cars!");
trafficLight.changeState(new YellowState());
}
}
// Context
class TrafficLight {
private State currentState;
public TrafficLight() {
currentState = new RedState();
}
public void changeState(State state) {
this.currentState = state;
}
public void handle() {
currentState.handle(this);
}
public static void main(String[] args) {
TrafficLight trafficLight = new TrafficLight();
trafficLight.handle();
trafficLight.handle();
trafficLight.handle();
trafficLight.handle();
}
}