定义/意图:Visitor用来表示一组特定元素上的操作,通过Visitors将行为分离出来,当需要在这组元素上增加新的操作时,只需要实现新的Visitor即可。
问题:系统有一组稳定的元素,但针对其的操作在以后会扩展,若元素的类型容易扩展,则不适合用Visit模式
解决方案:把元素和元素上的操作分离,将所有操作的接口抽象出来,
参与者和协作者:Visitor基类声明了针对所有具体元素类上的操作,操作的名称和参数指明了发送给Visitors的具体元素类型,这样Visitors可以在特定的接口中完成对元素的访问处理。Concreate Visitor实现了特定的访问元素的处理操作方法。Element定义了接受Vistor的处理接口。ObjectStruct可能是实现了组合模式的一组对象,以供遍历使用。
效果:在增加操作时可以方便扩展,而不影响已有数据元素
实现:
示例代码:
一个比较理解的现实例子为:1号大院住着两户人家A和B,经常有不同的客人去两家做一些不同的事情,比如1水厂去查水表收水费,2钟点工去两家做家务。
using System;
using System.Collections.Generic;
namespace Visitor
{
/// <summary>
/// MainApp startup class for Structural
/// Visitor Design Pattern.
/// </summary>
class MainApp
{
static void Main()
{
// Setup structure
ObjectStructure o = new ObjectStructure();
o.Attach(new ConcreteElementA());
o.Attach(new ConcreteElementB());
// Create visitor objects
ConcreteVisitor1 v1 = new ConcreteVisitor1();
ConcreteVisitor2 v2 = new ConcreteVisitor2();
// Structure accepting visitors
o.Accept(v1);
o.Accept(v2);
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'Visitor' abstract class
/// </summary>
abstract class Visitor
{
public abstract void VisitConcreteElementA(ConcreteElementA concreteElementA);
public abstract void VisitConcreteElementB(ConcreteElementB concreteElementB);
}
/// <summary>
/// A 'ConcreteVisitor' class
/// </summary>
class ConcreteVisitor1 : Visitor
{
public override void VisitConcreteElementA(ConcreteElementA concreteElementA)
{
Console.WriteLine("{0} visited by {1}",
concreteElementA.GetType().Name, this.GetType().Name);
}
public override void VisitConcreteElementB(ConcreteElementB concreteElementB)
{
Console.WriteLine("{0} visited by {1}",
concreteElementB.GetType().Name, this.GetType().Name);
}
}
/// <summary>
/// A 'ConcreteVisitor' class
/// </summary>
class ConcreteVisitor2 : Visitor
{
public override void VisitConcreteElementA(ConcreteElementA concreteElementA)
{
Console.WriteLine("{0} visited by {1}",
concreteElementA.GetType().Name, this.GetType().Name);
}
public override void VisitConcreteElementB(ConcreteElementB concreteElementB)
{
Console.WriteLine("{0} visited by {1}",
concreteElementB.GetType().Name, this.GetType().Name);
}
}
/// <summary>
/// The 'Element' abstract class
/// </summary>
abstract class Element
{
public abstract void Accept(Visitor visitor);
}
/// <summary>
/// A 'ConcreteElement' class
/// </summary>
class ConcreteElementA : Element
{
public override void Accept(Visitor visitor)
{
visitor.VisitConcreteElementA(this);
}
public void OperationA()
{
}
}
/// <summary>
/// A 'ConcreteElement' class
/// </summary>
class ConcreteElementB : Element
{
public override void Accept(Visitor visitor)
{
visitor.VisitConcreteElementB(this);
}
public void OperationB()
{
}
}
/// <summary>
/// The 'ObjectStructure' class
/// </summary>
class ObjectStructure
{
private List<Element> _elements = new List<Element>();
public void Attach(Element element)
{
_elements.Add(element);
}
public void Detach(Element element)
{
_elements.Remove(element);
}
public void Accept(Visitor visitor)
{
foreach (Element element in _elements)
{
element.Accept(visitor);
}
}
}
}
Visitor模式的主要问题是,Visitor中的方法都是面向实现的,当Elements扩展时,整个模式需要大量的修改。为了解决这个问题,对现有Visitor模式其进行了扩展。
改进一
增加一层抽象,并利用RTTI在运行时进行类型的转换,从而解决Element类型扩展难的问题。类图如下:
改进二
在改进一的基础上,采用反射的方式来获得Elements的类型,从而实现扩展。类图如下: