Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chain of Responsibility Pattern Medium

The chain of responsibility pattern avoids coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Handlers are chained together, and the request is passed along the chain until a handler processes it or the chain ends.

Implementation

package chain

// Request represents the data flowing through the chain.
type Request struct {
	Amount float64
}

// Handler defines the interface for a link in the chain.
type Handler interface {
	SetNext(Handler) Handler
	Handle(Request) string
}

// BaseHandler provides default chaining behavior.
type BaseHandler struct {
	next Handler
}

func (b *BaseHandler) SetNext(h Handler) Handler {
	b.next = h
	return h
}

func (b *BaseHandler) HandleNext(r Request) string {
	if b.next != nil {
		return b.next.Handle(r)
	}
	return "no handler approved the request"
}

// --- Concrete handlers ---

type Manager struct{ BaseHandler }

func (m *Manager) Handle(r Request) string {
	if r.Amount < 1000 {
		return "Manager approved"
	}
	return m.HandleNext(r)
}

type Director struct{ BaseHandler }

func (d *Director) Handle(r Request) string {
	if r.Amount < 5000 {
		return "Director approved"
	}
	return d.HandleNext(r)
}

type VP struct{ BaseHandler }

func (v *VP) Handle(r Request) string {
	if r.Amount < 10000 {
		return "VP approved"
	}
	return v.HandleNext(r)
}

Usage

manager := &chain.Manager{}
director := &chain.Director{}
vp := &chain.VP{}

manager.SetNext(director).SetNext(vp)

fmt.Println(manager.Handle(chain.Request{Amount: 500}))
// Manager approved

fmt.Println(manager.Handle(chain.Request{Amount: 3000}))
// Director approved

fmt.Println(manager.Handle(chain.Request{Amount: 8000}))
// VP approved

fmt.Println(manager.Handle(chain.Request{Amount: 50000}))
// no handler approved the request

Rules of Thumb

  • Use chain of responsibility when more than one object may handle a request, and the handler is determined at runtime.
  • The chain can be composed dynamically at runtime, making it easy to add, remove, or reorder handlers.
  • HTTP middleware stacks (e.g. in net/http) are a common real-world example of this pattern.