ឥរិយាបថ កម្រិតស្មុគស្មាញ: មធ្យម

State in Go

Change an object’s behavior when its internal state changes by delegating transitions and rules to state-specific types.

The Problem

Objects with life cycles often accumulate large switch statements over status fields. An order may allow payment only in one state, shipping in another, and cancellation in several. As the workflow grows, those branches become harder to test and easier to break.

The Solution

State moves behavior and transition rules into dedicated state objects. In Go, the context stores an interface for the current state and delegates stateful operations to it. Each concrete state decides whether an action is allowed and what the next state should be.

Structure

State Pattern
Step 1 of 6

The State Interface

OrderState declares the operations that vary by lifecycle stage: Pay, Ship, Cancel. Each concrete state implements what is legal and what transition comes next — no switch statements anywhere.

  • Context: Order stores the current state.
  • State interface: Declares the operations that vary by state.
  • Concrete states: Pending, paid, shipped, and cancelled states enforce different rules.
  • Client: Calls operations on the order without branching on status values.

Implementation

This example models a basic order workflow. Each state object owns the legal transitions, and the order delegates Pay, Ship, and Cancel to whichever state is active.

package main

type Order struct {
	ID    string
	state OrderState
}

func NewOrder(id string) *Order {
	return &Order{ID: id, state: PendingState{}}
}

func (o *Order) setState(state OrderState) {
	o.state = state
}

func (o *Order) Status() string {
	return o.state.Name()
}

func (o *Order) Pay() error {
	return o.state.Pay(o)
}

func (o *Order) Ship() error {
	return o.state.Ship(o)
}

func (o *Order) Cancel() error {
	return o.state.Cancel(o)
}

Real-World Analogy

Think of a traffic light. The same control box behaves differently depending on whether the current state is green, yellow, or red, and the legal next transition depends on that current state.

Pros and Cons

ProsCons
Localizes lifecycle-specific rules close to each state.Introduces more types and indirection than a simple switch.
Removes large status-based conditionals from the context object.Transition logic can become scattered across several state objects.
Makes invalid transitions easier to model explicitly.Not worth it for small workflows with only a few stable branches.

Best Practices

  • Use State when behavior truly changes by lifecycle stage, not just because a label exists.
  • Keep state interfaces focused on the operations that vary.
  • Let concrete states own transitions so invalid moves stay localized.
  • If state objects need no data, zero-sized structs keep the pattern lightweight in Go.
  • Do not introduce State if a small switch is still simpler and unlikely to grow.

When to Use

  • An object has a lifecycle with different allowed operations per stage.
  • Status-based conditionals are spreading across the codebase.
  • You want invalid transitions expressed close to the rules themselves.

When NOT to Use

  • There are only a couple of stable branches and they are unlikely to grow.
  • The transition logic is simpler as a small explicit switch.
  • The extra objects would hide rather than clarify the workflow.