State Design Pattern in Go

1. Definition

The State Design Pattern allows an object to change its behavior when its internal state changes, making it appear as though the object changed its class.

2. Problem Statement

How can an object's behavior change at runtime depending on its state, without resorting to large monolithic conditional statements or having to modify the object itself?

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. Instead of implementing state-specific behaviors directly within the context class, you delegate the work to an underlying state object.

4. Real-World Use Cases

1. A vending machine that has states like 'idle', 'has coin', and 'dispensing product'.

2. Online order processing system where orders can be in 'new', 'shipped', 'completed', or 'canceled' states.

3. Game character behaviors that change based on character health or abilities.

5. Implementation Steps

1. Define a state interface that will encompass methods representative of behavior changes.

2. Implement concrete state classes for each individual state.

3. The main class (context) will maintain a reference to the current state and delegate state-specific behavior to the current state object.

6. Implementation in Go

// State interface
type State interface {
	Handle(context *Context)
}
// Concrete state A
type ConcreteStateA struct {}
func (s *ConcreteStateA) Handle(context *Context) {
	fmt.Println("Currently in State A")
	context.state = &ConcreteStateB{}
}
// Concrete state B
type ConcreteStateB struct {}
func (s *ConcreteStateB) Handle(context *Context) {
	fmt.Println("Switched to State B")
	context.state = &ConcreteStateA{}
}
// Context class that maintains a reference to the current state
type Context struct {
	state State
}
func NewContext(initialState State) *Context {
	return &Context{state: initialState}
}
func (c *Context) Request() {
	c.state.Handle(c)
}
// Client code
func main() {
	context := NewContext(&ConcreteStateA{})
	for i := 0; i < 5; i++ {
		context.Request()
	}
}

Output:

Currently in State A
Switched to State B
Currently in State A
Switched to State B
Currently in State A

Explanation:

1. The State interface defines a method 'Handle' which will be implemented by all concrete states.

2. ConcreteStateA and ConcreteStateB are concrete implementations of the State interface. They change the context's state within their 'Handle' methods.

3. The Context class maintains a reference to its current state and can transition between states by calling the 'Handle' method.

4. In the client code, we instantiate the context with an initial state and then call the 'Request' method several times, leading to state transitions.

7. When to use?

The State Pattern is beneficial when:

1. An object's behavior is supposed to change dynamically based on its state.

2. Multiple states have shared transition logic and you want to avoid duplicating the transition code across these states.

3. States, transitions, and behaviors need to be defined or changed at runtime.


Comments