★☆☆☆☆
表示一个作用于某对象结构中的各元素的操作。它可以在不改变各元素的类的前提下定义作用于这些元素的新的操作。
Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
结构图
角色
- 抽象访问者(Visitor)角色:声明了一个或多个访问操作,形成所有的具体元素角色必须的接口。
- 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作。
- 元素(Element)角色:定义一个接受操作,它以一个访问者为参数。
- 具体元素(ConcreteElement)角色:实现一个接受操作,它以一个访问者为参数。
- 对象结构(ObjectStructure)角色:可以提供一个高层接口以允许该访问者访问它的元素;可以是一个复合对象或集合对象。
动机
在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给予类带来很繁重的变更负担,甚至破坏原有设计。。
如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构萨哈那个的各个类动态添加新的操作,从而避免上述问题?
意图
表示一个作用于某对象结构中的各元素的操作。它可以在不改变各元素的类的前提下定义作用于这些元素的新的操作。
示意性代码
结构代码
'Visitor pattern -- Structural example
Imports System
Imports System.Collection
'MainApp test application
Module MainApp
Public Sub Main
'Setup structure
Dim o as new objectstructure
o.Attach(new ConcreteElementA)
o.Attach(new ConcreteElementB)
'Create visitor objects
Dim v1 as new concreteVisitor1
dim v2 as new concretevisitor2
'Structure accepting visitors
o.Accept(v1)
o.Accept(v2)
'Wait for user
Console.ReadLine()
End Sub
End Module
'"Visitor"
public mustinherit class Visitor
public mustoverride sub VisitConcreteElementA( _
byval concreteElementA as concreteElementA)
public mustoverride sub VisitConcreteElementB( _
byval concreteElementB as concreteElementB)
end class
'"ConcreteVisitor1"
public class ConcreteVisitor1
inherits Visitor
public overrides sub VisitConcreteElementA( _
byval concreteElementA as concreteElementA)
Console.WriteLine("{0} visited by {1}", _
concreteElementA.GetType().Name,me.GetType().Name)
end sub
public overrides sub VisitConcreteElementB( _
byval concreteElementB as concreteElementB)
Console.WriteLine("{0} visited by {1}", _
concreteElementB.GetType().Name,me.GetType().Name)
end sub
end class
'"ConcreteVisitor2"
public class ConcreteVisitor2
inherits Visitor
public overrides sub VisitConcreteElementA( _
byval concreteElementA as concreteElementA)
Console.WriteLine("{0} visited by {1}", _
concreteElementA.GetType().Name,me.GetType().Name)
end sub
public overrides sub VisitConcreteElementB( _
byval concreteElementB as concreteElementB)
Console.WriteLine("{0} visited by {1}", _
concreteElementB.GetType().Name,me.GetType().Name)
end sub
end class
'"Element"
public mustinherit class Element
public mustoverride sub Accept(byval visitor as Visitor)
end class
'"ConcreteElementA"
public class ConcreteElementA
inherits Element
public overrides sub Accept(byval visitor as Visitor)
visitor.VisitConcreteElementA(me)
End sub
public sub OperationB()
End sub
end class
'"ConcreteElementB"
public class ConcreteElementB
inherits Element
public overrides sub Accept(byval visitor as Visitor)
visitor.VisitConcreteElementB(me)
End sub
public sub OperationB()
End sub
end class
'"ObjectStructure"
public class ObjectStructure
private elements as new ArrayList
public sub Attach(byval element as Element)
elements.Add(element)
end sub
public sub Detach(byval element as Element)
elements.remove(element)
end sub
public sub Accept(byval visitor as Visitor)
for each e as element in elements
e.Accept(visitor)
next
end sub
End Class
一个实例
以下的例子演示了Employee对象集合允许被不同的Visitor(IncomeVisitor与VacationVisitor)访问其中的内容。
实例代码
' Visitor pattern -- Real World example
Imports System
Imports System.Collections
' MainApp startup application
Module MainApp
Private Shared Sub Main()
' Setup employee collection
Dim e As New Employees()
e.Attach(New Clerk())
e.Attach(New Director())
e.Attach(New President())
' Employees are 'visited'
e.Accept(New IncomeVisitor())
e.Accept(New VacationVisitor())
' Wait for user
Console.Read()
End Sub
End Module
'"Visitor"
Public Interface IVisitor
Sub Visit(ByVal element As Element)
End Interface
'"ConcreteVisitor1"
Public Class IncomeVisitor
Implements IVisitor
Public Sub Visit(ByVal element As Element) Implements IVisitor.Visit
Dim employee As Employee = TryCast(element, Employee)
' Provide 10% pay raise
employee.Income *= 1.1
Console.WriteLine("{0} {1}'s new income: {2:C}", employee.[GetType]().Name, employee.Name, employee.Income)
End Sub
End Class
'"ConcreteVisitor2"
Public Class VacationVisitor
Implements IVisitor
Public Sub Visit(ByVal element As Element) Implements IVisitor.Visit
Dim employee As Employee = TryCast(element, Employee)
' Provide 3 extra vacation days
Console.WriteLine("{0} {1}'s new vacation days: {2}", employee.[GetType]().Name, employee.Name, employee.VacationDays)
End Sub
End Class
Public Class Clerk
Inherits Employee
' Constructor
Public Sub New()
MyBase.New("Hank", 25000, 14)
End Sub
End Class
Public Class Director
Inherits Employee
' Constructor
Public Sub New()
MyBase.New("Elly", 35000, 16)
End Sub
End Class
Public Class President
Inherits Employee
' Constructor
Public Sub New()
MyBase.New("Dick", 45000, 21)
End Sub
End Class
'"Element"
Public MustInherit Class Element
Public MustOverride Sub Accept(ByVal visitor As IVisitor)
End Class
'"ConcreteElement"
Public Class Employee
Inherits Element
Private m_name As String
Private m_income As Double
Private m_vacationDays As Integer
' Constructor
Public Sub New(ByVal name As String, ByVal income As Double, ByVal vacationDays As Integer)
Me.m_name = name
Me.m_income = income
Me.m_vacationDays = vacationDays
End Sub
' Properties
Public Property Name() As String
Get
Return m_name
End Get
Set
m_name = value
End Set
End Property
Public Property Income() As Double
Get
Return m_income
End Get
Set
m_income = value
End Set
End Property
Public Property VacationDays() As Integer
Get
Return m_vacationDays
End Get
Set
m_vacationDays = value
End Set
End Property
Public Overloads Overrides Sub Accept(ByVal visitor As IVisitor)
visitor.Visit(Me)
End Sub
End Class
'"ObjectStructure"
Public Class Employees
Private employees As New ArrayList()
Public Sub Attach(ByVal employee As Employee)
employees.Add(employee)
End Sub
Public Sub Detach(ByVal employee As Employee)
employees.Remove(employee)
End Sub
Public Sub Accept(ByVal visitor As IVisitor)
For Each e As Employee In employees
e.Accept(visitor)
Next
Console.WriteLine()
End Sub
End Class
Visitor Pattern模式的几个要点:
1、Visitor模式通过所谓双重分发(double dispatch)来实现在不更改Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作。
2、所谓双重分发即Visitor模式中间包括了两个多态分发(注意其中的多态机制):第一个为accept方法的多态辨析;第二个为visit方法的多态辨析。
3、Visitor模式的最大缺点在于扩展类层次结构(增添新的Element子类),会导致Visitor类的改变。因此Visitor模式适用于“Element类层次结构稳定,而其中的操作却经常面临频繁改动”。
我的理解
封装对象操作变化,支持在运行时为类层次结构动态添加新的操作。
参考资料
《C#面向对象设计模式纵横谈系列课程(24)》 李建中老师