引言
责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,用于将请求沿处理者链传递,直到找到合适的处理者。在 Go 语言中,凭借其简洁的接口和结构体组合,责任链模式的实现既优雅又高效。本文将通过一个原创的医疗诊断系统场景,详细讲解责任链模式的概念、实现和应用,带您从零到一掌握这一模式。
本文适合 Go 语言初学者、设计模式爱好者以及希望分享技术内容的开发者。让我们开始吧!
1. 什么是责任链模式?
责任链模式允许请求沿处理者链传递,每个处理者决定是否处理请求或传递给下一个处理者。它解耦了请求发送者和处理者,类似医院的诊断流程:患者带着症状就诊,初级医生检查后可能转诊给专科医生或专家,直到问题解决。
责任链模式的优点
- 解耦:发送者无需指定处理者。
- 灵活性:动态调整链条。
- 单一职责:每个处理者专注自己的职责。
- 可扩展:易于添加新处理者。
责任链模式的缺点
- 处理延迟:链条过长可能增加时间。
- 无人处理:可能导致请求未被处理。
- 调试复杂:链条复杂时追踪困难。
2. 责任链模式的核心角色
责任链模式包含以下角色:
- 抽象处理者(Handler):定义处理请求和设置链的接口。
- 具体处理者(Concrete Handler):处理特定请求或传递。
- 客户端(Client):发起请求。
在 Go 中,我们用接口定义处理者,用结构体实现具体处理者。
3. 医疗诊断系统场景
我们设计一个医疗诊断系统:
背景:患者输入症状(如“发烧”),系统将症状交给初级医生、专科医生或专家诊断。每个医生尝试处理,无法处理则转诊。系统记录诊断日志。
目标:通过责任链模式,实现动态症状诊断和日志记录。
4. Go 语言实现
下面,我们分步骤实现责任链模式。
4.1 定义抽象处理者
定义诊断接口 DiagnosisHandler
。
1
2
3
4
5
6
7
|
package main
// DiagnosisHandler 是抽象处理者接口,定义诊断和链条设置方法
type DiagnosisHandler interface {
Diagnose(symptom string) string
SetNext(handler DiagnosisHandler)
}
|
说明:
Diagnose
处理症状,SetNext
设置下一个处理者。
- Go 的隐式接口简化实现。
4.2 定义基础处理者
实现基础处理者 BaseDiagnosisHandler
,提供默认行为。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package main
import "fmt"
// BaseDiagnosisHandler 提供默认的处理者行为
type BaseDiagnosisHandler struct {
next DiagnosisHandler
name string // 处理者名称(如“初级医生”)
}
// NewBaseDiagnosisHandler 创建基础处理者
func NewBaseDiagnosisHandler(name string) *BaseDiagnosisHandler {
return &BaseDiagnosisHandler{name: name}
}
// SetNext 设置链中的下一个处理者
func (h *BaseDiagnosisHandler) SetNext(handler DiagnosisHandler) {
h.next = handler
}
// Diagnose 默认诊断行为:记录日志并传递给下一个处理者
func (h *BaseDiagnosisHandler) Diagnose(symptom string) string {
// 记录诊断日志
fmt.Printf("Doctor %s is examining symptom: %s\n", h.name, symptom)
// 如果有下一个处理者,传递请求
if h.next != nil {
return h.next.Diagnose(symptom)
}
// 如果没有下一个处理者,返回无法诊断
return "No diagnosis available for symptom: " + symptom
}
|
说明:
- 提供默认日志和传递逻辑。
next
持有链中下一个处理者。
4.3 定义具体处理者
为初级医生、专科医生和专家创建处理者。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
package main
// PrimaryDoctor 初级医生,处理常见症状
type PrimaryDoctor struct {
*BaseDiagnosisHandler
}
// NewPrimaryDoctor 创建初级医生
func NewPrimaryDoctor() *PrimaryDoctor {
return &PrimaryDoctor{BaseDiagnosisHandler: NewBaseDiagnosisHandler("Primary Doctor")}
}
// Diagnose 处理发烧和头痛
func (d *PrimaryDoctor) Diagnose(symptom string) string {
fmt.Printf("Primary Doctor is examining symptom: %s\n", symptom)
if symptom == "fever" {
return "Diagnosis by Primary Doctor: Treat with rest and hydration."
}
if symptom == "headache" {
return "Diagnosis by Primary Doctor: Recommend pain reliever."
}
return d.BaseDiagnosisHandler.Diagnose(symptom)
}
// SpecialistDoctor 专科医生,处理咳嗽和喉咙痛
type SpecialistDoctor struct {
*BaseDiagnosisHandler
}
// NewSpecialistDoctor 创建专科医生
func NewSpecialistDoctor() *SpecialistDoctor {
return &SpecialistDoctor{BaseDiagnosisHandler: NewBaseDiagnosisHandler("Specialist Doctor")}
}
// Diagnose 处理咳嗽和喉咙痛
func (d *SpecialistDoctor) Diagnose(symptom string) string {
fmt.Printf("Specialist Doctor is examining symptom: %s\n", symptom)
if symptom == "cough" {
return "Diagnosis by Specialist Doctor: Prescribe cough syrup."
}
if symptom == "sore throat" {
return "Diagnosis by Specialist Doctor: Recommend throat lozenges."
}
return d.BaseDiagnosisHandler.Diagnose(symptom)
}
// ExpertDoctor 专家,处理复杂症状
type ExpertDoctor struct {
*BaseDiagnosisHandler
}
// NewExpertDoctor 创建专家
func NewExpertDoctor() *ExpertDoctor {
return &ExpertDoctor{BaseDiagnosisHandler: NewBaseDiagnosisHandler("Expert Doctor")}
}
// Diagnose 处理复杂症状
func (d *ExpertDoctor) Diagnose(symptom string) string {
fmt.Printf("Expert Doctor is examining symptom: %s\n", symptom)
if symptom == "chest pain" {
return "Diagnosis by Expert Doctor: Refer to cardiologist for further tests."
}
return d.BaseDiagnosisHandler.Diagnose(symptom)
}
|
说明:
- 每个处理者嵌入
BaseDiagnosisHandler
,复用默认行为。
- 处理特定症状,否则传递请求。
4.4 客户端代码
模拟医疗系统处理症状。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package main
import "fmt"
// MedicalSystem 模拟医疗诊断系统
type MedicalSystem struct {
handler DiagnosisHandler
}
// NewMedicalSystem 创建医疗系统,设置责任链
func NewMedicalSystem() *MedicalSystem {
// 创建处理者
primary := NewPrimaryDoctor()
specialist := NewSpecialistDoctor()
expert := NewExpertDoctor()
// 设置责任链:初级医生 -> 专科医生 -> 专家
primary.SetNext(specialist)
specialist.SetNext(expert)
return &MedicalSystem{handler: primary}
}
// DiagnosePatient 发起诊断请求
func (m *MedicalSystem) DiagnosePatient(symptom string) string {
fmt.Printf("\nPatient with symptom '%s' enters the system:\n", symptom)
return m.handler.Diagnose(symptom)
}
func main() {
// 创建医疗系统
system := NewMedicalSystem()
// 测试不同症状
symptoms := []string{"fever", "cough", "chest pain", "fatigue"}
for _, symptom := range symptoms {
result := system.DiagnosePatient(symptom)
fmt.Println("Result:", result)
fmt.Println()
}
}
|
说明:
- 构建责任链并测试不同症状。
- 客户端只与链的起点交互。
4.5 运行结果
运行代码,输出:
Patient with symptom 'fever' enters the system:
Primary Doctor is examining symptom: fever
Result: Diagnosis by Primary Doctor: Treat with rest and hydration.
Patient with symptom 'cough' enters the system:
Primary Doctor is examining symptom: cough
Specialist Doctor is examining symptom: cough
Result: Diagnosis by Specialist Doctor: Prescribe cough syrup.
Patient with symptom 'chest pain' enters the system:
Primary Doctor is examining symptom: chest pain
Specialist Doctor is examining symptom: chest pain
Expert Doctor is examining symptom: chest pain
Result: Diagnosis by Expert Doctor: Refer to cardiologist for further tests.
Patient with symptom 'fatigue' enters the system:
Primary Doctor is examining symptom: fatigue
Specialist Doctor is examining symptom: fatigue
Expert Doctor is examining symptom: fatigue
Result: No diagnosis available for symptom: fatigue
5. 扩展:添加心理医生
添加“心理医生”处理“anxiety”症状。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package main
// PsychologistDoctor 心理医生,处理心理症状
type PsychologistDoctor struct {
*BaseDiagnosisHandler
}
// NewPsychologistDoctor 创建心理医生
func NewPsychologistDoctor() *PsychologistDoctor {
return &PsychologistDoctor{BaseDiagnosisHandler: NewBaseDiagnosisHandler("Psychologist Doctor")}
}
// Diagnose 处理心理症状
func (d *PsychologistDoctor) Diagnose(symptom string) string {
fmt.Printf("Psychologist Doctor is examining symptom: %s\n", symptom)
if symptom == "anxiety" {
return "Diagnosis by Psychologist Doctor: Recommend counseling sessions."
}
return d.BaseDiagnosisHandler.Diagnose(symptom)
}
|
更新 main.go
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
package main
import "fmt"
// MedicalSystem 模拟医疗诊断系统
type MedicalSystem struct {
handler DiagnosisHandler
}
// NewMedicalSystem 创建医疗系统,设置责任链
func NewMedicalSystem() *MedicalSystem {
// 创建处理者
primary := NewPrimaryDoctor()
specialist := NewSpecialistDoctor()
expert := NewExpertDoctor()
psychologist := NewPsychologistDoctor()
// 设置责任链:初级医生 -> 专科医生 -> 专家 -> 心理医生
primary.SetNext(specialist)
specialist.SetNext(expert)
expert.SetNext(psychologist)
return &MedicalSystem{handler: primary}
}
// DiagnosePatient 发起诊断请求
func (m *MedicalSystem) DiagnosePatient(symptom string) string {
fmt.Printf("\nPatient with symptom '%s' enters the system:\n", symptom)
return m.handler.Diagnose(symptom)
}
func main() {
// 创建医疗系统
system := NewMedicalSystem()
// 测试不同症状
symptoms := []string{"fever", "cough", "chest pain", "anxiety", "fatigue"}
for _, symptom := range symptoms {
result := system.DiagnosePatient(symptom)
fmt.Println("Result:", result)
fmt.Println()
}
}
|
新增输出:
Patient with symptom 'anxiety' enters the system:
Primary Doctor is examining symptom: anxiety
Specialist Doctor is examining symptom: anxiety
Expert Doctor is examining symptom: anxiety
Psychologist Doctor is examining symptom: anxiety
Result: Diagnosis by Psychologist Doctor: Recommend counseling sessions.
说明:
- 新增处理者无需修改现有代码。
- 责任链动态扩展,符合开闭原则。
6. Go 语言中的责任链特性
Go 的责任链模式特点:
- 隐式接口:简化实现。
- 结构体组合:复用默认行为。
- 动态链条:支持动态调整。
- 指针语义:确保引用传递。
与 Java 相比,Go 的实现更轻量,但需手动管理链条。
7. 应用场景
责任链模式适用于:
- 事件处理:GUI 事件传递。
- 请求处理:Web 中间件。
- 审批流程:逐级审批。
- 诊断系统:症状诊断。
- 错误处理:错误传递。
8. 最佳实践与注意事项
最佳实践
- 精简接口:只定义必要方法。
- 默认行为:提供基类逻辑。
- 动态链条:支持灵活调整。
- 日志:记录处理过程。
- 测试:确保链条正确。
注意事项
9. 总结
责任链模式解耦了请求和处理者,适合动态分配职责的场景。在 Go 中通过接口和结构体实现简洁灵活。本文通过医疗诊断系统,展示了症状的逐级诊断,体现了模式的实用性。
评论 0