What It Is
The Chain of Responsibility pattern passes a request along a chain of handlers. Each handler either deals with the request or hands it to the next one. The sender only knows the first link — never who will actually solve it.
Example: A Support Line
A problem enters at the chat bot. What the bot cannot fix goes to tech support; what tech support cannot fix goes to the manager, who handles everything that arrives.
Pass it on until someone can
"No internet" travels one step and stops at tech support; the refund request travels the whole chain to the manager. The caller wrote the same one line both times.
Output: Go program and captured output
package main
import "fmt"
type Handler struct {
name string
canFix func(problem string) bool
next *Handler
}
func (h *Handler) Handle(problem string) {
if h.canFix(problem) {
fmt.Println(h.name, "solved:", problem)
return
}
fmt.Println(h.name, "passes it on")
if h.next != nil {
h.next.Handle(problem)
}
}
func main() {
manager := &Handler{
name: "manager",
canFix: func(p string) bool { return true },
}
tech := &Handler{
name: "tech support",
canFix: func(p string) bool { return p == "no internet" },
next: manager,
}
bot := &Handler{
name: "chat bot",
canFix: func(p string) bool { return p == "forgot password" },
next: tech,
}
bot.Handle("no internet")
fmt.Println("---")
bot.Handle("refund request")
}
When to Use It
Use it when several parties might handle a request and the right one is not known in advance — support escalation, middleware, event handling, approval workflows.
Skip it when the handler is always known — call it directly. A chain with one link, or one that always ends at the same link, is a plain function call in costume.
Pros and Cons
Pros: sender and receivers are decoupled; links can be added, removed and reordered freely; each handler stays small.
Cons: no guarantee anyone handles the request — a broken chain swallows it silently; following a request through many links makes debugging slower.