Golang - Chain of Responsibility Pattern

Golang - Chain of Responsibility Pattern

The Chain of Responsibility pattern is a behavioral design pattern that allows an object to pass a request along a chain of handlers until one of the handlers can handle the request. This pattern is useful when there are multiple objects that can handle a request, but we do not know which object can handle the request until runtime.

The Gang of Four, in their book "Design Patterns: Elements of Reusable Object-Oriented Software", first introduced the Chain of Responsibility pattern.

Implementation in Golang

Let's implement the Chain of Responsibility pattern in Golang. We will use a realistic example where a customer's order is handled by different departments in a company.

type Order struct {
    // ...
}

type OrderHandler interface {
    SetNext(OrderHandler) OrderHandler
    Handle(*Order) error
}

type OrderProcessor struct {
    handler OrderHandler
}

func (o *OrderProcessor) SetHandler(handler OrderHandler) {
    o.handler = handler
}

func (o *OrderProcessor) Process(order *Order) error {
    return o.handler.Handle(order)
}

type WarehouseHandler struct {
    next OrderHandler
}

func (w *WarehouseHandler) SetNext(next OrderHandler) OrderHandler {
    w.next = next
    return next
}

func (w *WarehouseHandler) Handle(order *Order) error {
    if order.WarehouseFilled {
        if w.next != nil {
            return w.next.Handle(order)
        }
        return nil
    }
    return errors.New("warehouse not filled")
}

type ShippingHandler struct {
    next OrderHandler
}

func (s *ShippingHandler) SetNext(next OrderHandler) OrderHandler {
    s.next = next
    return next
}

func (s *ShippingHandler) Handle(order *Order) error {
    if order.ShippingFilled {
        if s.next != nil {
            return s.next.Handle(order)
        }
        return nil
    }
    return errors.New("shipping not filled")
}

type BillingHandler struct {
    next OrderHandler
}

func (b *BillingHandler) SetNext(next OrderHandler) OrderHandler {
    b.next = next
    return next
}

func (b *BillingHandler) Handle(order *Order) error {
    if order.BillingFilled {
        if b.next != nil {
            return b.next.Handle(order)
        }
        return nil
    }
    return errors.New("billing not filled")
}

In the above code, we have defined the interface OrderHandler and a struct OrderProcessor. The OrderProcessor has a reference to an implementation of the OrderHandler interface.

We have also defined different handlers for different departments - WarehouseHandler, ShippingHandler, and BillingHandler. Each handler has a reference to the next handler in the chain.

The Handle function in each handler checks if the specific department has completed the order. If it has, it passes the request to the next handler in the chain. If not, it returns an error.

Usage

Let's see how we can use the Chain of Responsibility pattern in our example.

func main() {
    order := &Order{ /* ... */ }
    orderProcessor := &OrderProcessor{}

    warehouseHandler := &WarehouseHandler{}
    shippingHandler := &ShippingHandler{}
    billingHandler := &BillingHandler{}

    warehouseHandler.SetNext(shippingHandler)
    shippingHandler.SetNext(billingHandler)

    orderProcessor.SetHandler(warehouseHandler)

    if err := orderProcessor.Process(order); err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("Order processed successfully")
}

In the above code, we have created an order and an instance of the OrderProcessor. We have also created instances of the different handlers and set up the chain of responsibility.

Finally, we pass the order to the OrderProcessor and check if there were any errors.

Conclusion

The Chain of Responsibility pattern is a powerful pattern that allows us to handle requests in a flexible and extensible way. In Golang, we can implement this pattern using interfaces and structs, as shown in the example above.


References

Did you find this article valuable?

Support Matthias Bruns by becoming a sponsor. Any amount is appreciated!