结构图

角色
- 抽象处理者(Subject)角色:定义出一个处理请求的接口。 根据需要可以定义出一个设定和返回对下家引用的方法。这个角色通常由一个抽象类或接口实现。
- 代理主题(Proxy)角色:处理可以处理的请求,将不能处理的请求传递给下家。
动机
在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显式指定,将必不可少地带来请求发送者与接受者的紧耦合。如何使请求的发送者不需要指定具体的接受者?让请求的接受者自己在运行时决定来处理请求,从而使两者解耦。
意图
为使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
示意性代码


'Chain of Responsibility pattern -- Structural example
Public Class MainApp
'MainApp test application
Public Shared Sub Main()
'Setup Chain of Responsibility
Dim h1 As Handler = New ConcreteHandler1
Dim h2 As Handler = New ConcreteHandler2
Dim h3 As Handler = New ConcreteHandler3
h1.SetSuccessor(h2)
h2.SetSuccessor(h3)
'Generate and process request
Dim requests() As Integer = {2, 5, 14, 22, 18, 3, 27, 20}
For Each request As Integer In requests
h1.HandleRequest(request)
Next
'Wait for user
Console.ReadLine()
End Sub
End Class
'"Handler"
Public MustInherit Class Handler
Protected successor As Handler
Public Sub SetSuccessor(ByVal successor As Handler)
Me.successor = successor
End Sub
Public MustOverride Sub HandleRequest(ByVal request As Integer)
End Class
'"ConcreteHandler"
Public Class ConcreteHandler1
Inherits Handler
Public Overrides Sub HandleRequest(ByVal request As Integer)
If request > 0 And request < 10 Then
Console.WriteLine("{0} handled request {1}", Me.GetType().Name, request)
ElseIf successor IsNot Nothing Then
successor.HandleRequest(request)
End If
End Sub
End Class
'"ConcreteHandler"
Public Class ConcreteHandler2
Inherits Handler
Public Overrides Sub HandleRequest(ByVal request As Integer)
If request > 10 And request < 20 Then
Console.WriteLine("{0} handled request {1}", Me.GetType().Name, request)
ElseIf successor IsNot Nothing Then
successor.HandleRequest(request)
End If
End Sub
End Class
'"ConcreteHandler"
Public Class ConcreteHandler3
Inherits Handler
Public Overrides Sub HandleRequest(ByVal request As Integer)
If request > 20 And request < 30 Then
Console.WriteLine("{0} handled request {1}", Me.GetType().Name, request)
ElseIf successor IsNot Nothing Then
successor.HandleRequest(request)
End If
End Sub
End Class
一个实例
下面的责任链代码演示了不同职务的人根据所设定的权限对一个购买请求作出决策或将其交给更高的决策者。


'MainApp test application
Public Class MainApp
Private Shared Sub Main()
'Setup Chain of Responsibility
Dim Larry As New Director()
Dim Sam As New VicePresident()
Dim Tammy As New President()
Larry.SetSuccessor(Sam)
Sam.SetSuccessor(Tammy)
'Generate and process purchase requests
Dim p As New Purchase(2034, 350, "Supplies")
Larry.ProcessRequest(p)
p = New Purchase(2035, 32590.1, "Project X")
Larry.ProcessRequest(p)
p = New Purchase(2036, 122100, "Project Y")
Larry.ProcessRequest(p)
' Wait for user
Console.Read()
End Sub
End Class
' "Handler"
Public MustInherit Class Approver
Protected successor As Approver
Public Sub SetSuccessor(ByVal successor As Approver)
Me.successor = successor
End Sub
Public MustOverride Sub ProcessRequest(ByVal purchase As Purchase)
End Class
'"ConcreteHandler"
Public Class Director
Inherits Approver
Public Overloads Overrides Sub ProcessRequest(ByVal purchase As Purchase)
If purchase.Amount < 10000 Then
Console.WriteLine("{0} approved request# {1}", Me.[GetType]().Name, purchase.Number)
ElseIf successor IsNot Nothing Then
successor.ProcessRequest(purchase)
End If
End Sub
End Class
'"ConcreteHandler"
Public Class VicePresident
Inherits Approver
Public Overloads Overrides Sub ProcessRequest(ByVal purchase As Purchase)
If purchase.Amount < 25000 Then
Console.WriteLine("{0} approved request# {1}", Me.[GetType]().Name, purchase.Number)
ElseIf successor IsNot Nothing Then
successor.ProcessRequest(purchase)
End If
End Sub
End Class
'"ConcreteHandler"
Public Class President
Inherits Approver
Public Overloads Overrides Sub ProcessRequest(ByVal purchase As Purchase)
If purchase.Amount < 100000 Then
Console.WriteLine("{0} approved request# {1}", Me.[GetType]().Name, purchase.Number)
Else
Console.WriteLine("Request# {0} requires an executive meeting!", purchase.Number)
End If
End Sub
End Class
'Request details
Public Class Purchase
Private m_number As Integer
Private m_amount As Double
Private m_purpose As String
'Constructor
Public Sub New(ByVal number As Integer, ByVal amount As Double, ByVal purpose As String)
Me.m_number = number
Me.m_amount = amount
Me.m_purpose = purpose
End Sub
'Properties
Public Property Amount() As Double
Get
Return m_amount
End Get
Set(ByVal value As Double)
m_amount = value
End Set
End Property
Public Property Purpose() As String
Get
Return m_purpose
End Get
Set(ByVal value As String)
m_purpose = value
End Set
End Property
Public Property Number() As Integer
Get
Return m_number
End Get
Set(ByVal value As Integer)
m_number = value
End Set
End Property
End Class
Chain of Responsibility模式的几个要点:
1、Chain of Responsibility模式的应用场合在于“一个请求可能有多个接受者,但是最后真正的接收者只有一个,只有这时候请求发送者与接收者的耦合才有可能出现“变化脆弱”的症状,职责链的目的就是将二者解耦,从而更好的应对变化。
2、应用了Chain of Responsibility模式后,对象的职责分派将更具灵活性。我们可以在运行时动态添加/修改请求的处理职责。
3、如果请求传递到职责链的末尾仍得不到处理,应该有一个合理的缺省机制,这也是每一个接受对象的责任,而不是发出请求的对象的责任。
我的理解
封装对象责任,支持责任的变化。
参考资料
《C#设计模式(17)-Chain of Responsibility Pattern》 吕震宇
《C#面向对象设计模式纵横谈系列课程(14)》 李建中老师