Implementing the Chain of Responsibility Pattern in Go
This article explains the Chain of Responsibility design pattern, illustrates it with a company expense‑approval scenario, and provides complete Go code—including an interface, Staff, Manager, and CEO structs—showing how to build a flexible, extensible approval chain.
Chain Of Responsibility Pattern
Definition
To avoid coupling the request sender with multiple handlers, all handlers are linked together by each object holding a reference to the next one. When a request occurs, it travels along the chain until an object handles it.
Understanding
The functional modules are linked together like a linked list.
Imagine a typical company expense‑approval scenario.
Based on the flowchart we build a simple example with three approval roles: Staff (≤ 100), Manager (≤ 1000), and CEO (≥ 1000). The naïve pseudo‑code is:
现在我们有一笔钱需要审批,金额为x
if x<=100 {
"员工审批通过"
return
} else if x<=1000 {
"员工无权审批,经理审批通过"
return
} else {
"经理无权审批,CEO审批通过"
}
returnThis works for simple logic, but adding more roles forces continual changes to the conditional code, which becomes error‑prone. The structure actually forms a chain where each approver passes the request upward if it cannot handle it.
Code
First define an interface that declares the responsibility method.
type ChainResponsibility interface {
Approve(money int)
}Staff role implementation:
type Staff struct {
next ChainResponsibility // holds reference to the next approver
}
func (t Staff) Approve(money int) {
if money >= 0 && money < 100 {
fmt.Println("Staff审批完成")
} else {
fmt.Println("Staff无权限审批,传递给上一级")
t.next.Approve(money)
}
return
}Manager role implementation:
type Manager struct {
next ChainResponsibility // holds reference to the next approver
}
func (t Manager) Approve(money int) {
if money >= 100 && money < 1000 {
fmt.Println("Manager审批完成")
} else {
fmt.Println("Manager无权限审批,传递给上一级")
t.next.Approve(money)
}
return
}CEO role implementation (last in the chain):
type CEO struct {
// already the last element, no next reference needed
}
func (t CEO) Approve(money int) {
if money >= 1000 {
fmt.Println("CEO审批完成")
} else {
fmt.Println("CEO驳回审批")
}
return
}Invocation builds the chain and runs two approvals:
func main() {
s := &Staff{next: &Manager{next: &CEO{}}}
s.Approve(100)
fmt.Println("--------------")
s.Approve(1000)
return
} Staff无权限审批,传递给上一级
Manager审批完成
--------------
Staff无权限审批,传递给上一级
Manager无权限审批,传递给上一级
CEO审批完成The key line that constructs the chain is: s := &Staff{next: &Manager{next: &CEO{}}} After the chain is built, the client only calls the first handler; the request propagates automatically. Adding a new role later only requires a new struct and inserting it into the chain, demonstrating the pattern’s extensibility and maintainability.
Source code address:
https://github.com/gofish2020/gopattern/tree/main/behaviour/responsibility
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Nullbody Notes
Go backend development, learning open-source project source code together, focusing on simplicity and practicality.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
