State pattern implements completely different behavior of an object based on some state. This pattern is normally used for heavy processing multiple-step operations.
This article demonstrates State pattern implementations in Golang. Check the following examples.
Implementation
Follow the steps below for State pattern implementation in Go-
- Create an interface to ensure a common interface for all the state structs.
- Create a struct for the base state. Define a method for generating a new state struct. Embed the context and state interface into the state struct.
- Create state structs for different states. Embed the state base struct into this, to use the common fields and methods. Define methods declared in the interface.
- Define struct for context. This context struct should have methods for setting/chaing states. Also define methods for calling methods from state, as the state is a reference to the current state.
Here is a simple example of state pattern implementation-
// State pattern implementation in Go
package main
import "fmt"
// State interface and struct
type IState interface {
ActionOne()
ActionTwo()
}
type State struct {
context *Context
IState
}
func NewState(iState IState, context *Context) (state *State) {
state = &State{}
state.IState = iState
state.context = context
context.SetState(state)
return
}
// First Concrete State Struct
type ConcreteStateOne struct {
*State
}
func NewConcreteStateOne(context *Context) (concreteStateOne *ConcreteStateOne) {
concreteStateOne = &ConcreteStateOne{}
concreteStateOne.State = NewState(concreteStateOne, context)
return
}
func (concreteStateOne *ConcreteStateOne) ActionOne() {
fmt.Println("Calling 'actionOne' of - 'ConcreteStateOne'")
}
func (concreteStateOne *ConcreteStateOne) ActionTwo() {
fmt.Println("Calling 'actionTwo' of - 'ConcreteStateOne'")
}
// Second Concrete State Struct
type ConcreteStateTwo struct {
*State
}
func NewConcreteStateTwo(context *Context) (concreteStateTwo *ConcreteStateTwo) {
concreteStateTwo = &ConcreteStateTwo{}
concreteStateTwo.State = NewState(concreteStateTwo, context)
return
}
func (concreteStateTwo *ConcreteStateTwo) ActionOne() {
fmt.Println("Calling 'actionOne' of - 'ConcreteStateTwo'")
}
func (concreteStateTwo *ConcreteStateTwo) ActionTwo() {
fmt.Println("Calling 'actionTwo' of - 'ConcreteStateTwo'")
}
// Context Struct
type Context struct {
state *State
}
func NewContext() (context *Context) {
context = &Context{}
return
}
func (context *Context) GetState() *State {
return context.state
}
func (context *Context) PerformActionOne() {
context.state.ActionOne()
}
func (context *Context) PerformActionTwo() {
context.state.ActionTwo()
}
func (context *Context) SetState(state *State) {
context.state = state
}
// Demo
func main() {
context := NewContext()
NewConcreteStateOne(context)
context.PerformActionOne()
NewConcreteStateTwo(context)
context.PerformActionOne()
context.PerformActionTwo()
}
Output of this will be as below-
Calling 'actionOne' of - 'ConcreteStateOne'
Calling 'actionOne' of - 'ConcreteStateTwo'
Calling 'actionTwo' of - 'ConcreteStateTwo'
Examples
Here are a few examples of state pattern implementation in Golang-
Example #1: Order State Change
In this example, we are processing an order. We have a processing context struct, and we have structs for processing for each step.
Based on the state change, we are changing the context, and this way next steps are being processed until it reaches the delivered state.
Order Struct
- Create file “order_state.go”.
- Define interface “IOrderState”. Declare method “Process”, this will be used for processing of each step.
- Define struct “OrderState”. Embed “OrderContext” and “IOrderState” into this struct.
- Create method “NewOrderState”, which accepts “IOrderState” and “OrderContext” param. Initiate a “OrderState” sruct in this method, set the “IOrderState” and “OrderContext”.
// order_state.go
package main
type IOrderState interface {
Process()
}
type OrderState struct {
context *OrderContext
IOrderState
}
func NewOrderState(iOrderState IOrderState, context *OrderContext) (orderState *OrderState) {
orderState = &OrderState{}
orderState.IOrderState = iOrderState
orderState.context = context
context.SetState(orderState)
return
}
Order Check Struct
- Create file “order_check_state.go”.
- Define struct “OrderCheckState”.
- Embed “OrderState” into this struct.
- Define method “NewOrderCheckState” to create a new structs. Also set the OrderState with a new “OrderState” struct initiation.
- Implement interface “IOrderState”. Define method “Process” as part of the implementation. In the method implementation set the next state to in progress.
// order_check_state.go
package main
import "fmt"
type OrderCheckState struct {
*OrderState
}
func NewOrderCheckState(context *OrderContext) (orderCheckState *OrderCheckState) {
orderCheckState = &OrderCheckState{}
orderCheckState.OrderState = NewOrderState(orderCheckState, context)
return
}
func (orderCheckState *OrderCheckState) Process() {
fmt.Println("Checking the order validity and other information")
orderCheckState.context.SetState(orderCheckState.context.GetOrderInProgressState())
}
Order In-Progress Struct
- Create file “order_in_progress_state.go”.
- Define struct “OrderInProgress”.
- Define method “NewOrderInPRogressState”.
- Implement “IOrderState” interface, and define “Process” method for the interface implementation.
// order_in_progress_state.go
package main
import "fmt"
type OrderInProgressState struct {
*OrderState
}
func NewOrderInProgressState(context *OrderContext) (orderInProgressState *OrderInProgressState) {
orderInProgressState = &OrderInProgressState{}
orderInProgressState.OrderState = NewOrderState(orderInProgressState, context)
return
}
func (orderInProgressState *OrderInProgressState) Process() {
fmt.Println("Processing the order")
orderInProgressState.context.SetState(orderInProgressState.context.GetOrderDeliverState())
}
Order Deliver Struct
- Create file “order_deliver_state.go”.
- Define struct “OrderDeliverState”. Define method “NewOrderDeliverState” for new struct initiation. And define “Process” for implementing “IOrderState” interface.
// order_deliver_state.go
package main
import "fmt"
type OrderDeliverState struct {
*OrderState
}
func NewOrderDeliverState(context *OrderContext) (orderDeliverState *OrderDeliverState) {
orderDeliverState = &OrderDeliverState{}
orderDeliverState.OrderState = NewOrderState(orderDeliverState, context)
return
}
func (orderDeliverState *OrderDeliverState) Process() {
fmt.Println("Delivering the order")
orderDeliverState.context.SetState(orderDeliverState.context.GetOrderReceiveState())
}
Order Receive Struct
- Create file “order_receive_state.go”.
- Define struct “OrderReceiveState”. Define method “NewOrderReceiveState” for new struct initiation. And define “Process” for implementing “IOrderState” interface.
// order_receive_state.go
package main
import "fmt"
type OrderReceiveState struct {
*OrderState
}
func NewOrderReceiveState(context *OrderContext) (orderReceiveState *OrderReceiveState) {
orderReceiveState = &OrderReceiveState{}
orderReceiveState.OrderState = NewOrderState(orderReceiveState, context)
return
}
func (orderReceiveState *OrderReceiveState) Process() {
fmt.Println("Order received")
orderReceiveState.context.SetState(nil)
}
Order Context Struct
- Create file “order_context.go”.
- Define struct “OrderContext”.
- Declare field state(of type IOrderState), and one field for each state and define their types as “IOrderState”.
- Define method “NewOrderContext” to create/initiate a new struct “OrderContext”. Set the fields with relevant structs. Set the “state” field with the initial order state, which is check state.
- Define methods for the struct for getting and setting the state – “GetState” and “SetState”. Also, define methods for getting states in each step of order processing.
- Define method “RunNextProcess” for moving the order state to the next step.
// order_context.go
package main
import "fmt"
type OrderContext struct {
state IOrderState
orderCheckState IOrderState
orderInProgressState IOrderState
orderDeliverState IOrderState
orderReceiveState IOrderState
}
func NewOrderContext() (orderContext *OrderContext) {
orderContext = &OrderContext{}
orderContext.orderCheckState = NewOrderCheckState(orderContext)
orderContext.orderInProgressState = NewOrderInProgressState(orderContext)
orderContext.orderDeliverState = NewOrderDeliverState(orderContext)
orderContext.orderReceiveState = NewOrderReceiveState(orderContext)
orderContext.state = orderContext.orderCheckState
return
}
func (orderContext *OrderContext) GetOrderCheckState() IOrderState {
return orderContext.orderCheckState
}
func (orderContext *OrderContext) GetOrderInProgressState() IOrderState {
return orderContext.orderInProgressState
}
func (orderContext *OrderContext) GetOrderDeliverState() IOrderState {
return orderContext.orderDeliverState
}
func (orderContext *OrderContext) GetOrderReceiveState() IOrderState {
return orderContext.orderReceiveState
}
func (orderContext *OrderContext) GetState() IOrderState {
return orderContext.state
}
func (orderContext *OrderContext) SetState(state IOrderState) {
orderContext.state = state
}
func (orderContext *OrderContext) RunNextProcess() {
if orderContext.state != nil {
orderContext.state.Process()
} else {
fmt.Println("Order processing complete")
}
}
Demo
Create a new struct of “OrderContext” using “NewOrderContext”.
Then call the method “RunNextProcess” of that object. As the order is in the check state, so checking is performed and then the state is changed to the next state for processing.
Calling “RunNextPorcess” again will perform the processing and change the state to the delivering state.
The process goes on and the state keeps changing until the order reaches the last state.
// main.go
package main
func main() {
order := NewOrderContext()
order.RunNextProcess()
order.RunNextProcess()
order.RunNextProcess()
order.RunNextProcess()
// Trying to process after all steps are complete
order.RunNextProcess()
}
Output
Output will be as below-
Checking the order validity and other information
Processing the order
Delivering the order
Order received
Order processing complete
Source Code
Use the following link to get the source code:
Example | Source Code Link |
---|---|
Example #1: Order State Change | GitHub |
Other Code Implementations
Use the following links to check State pattern implementation in other programming languages.