定义/意图: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的类型,从而实现扩展。类图如下:
本文介绍访客模式的基本概念,如何将元素与操作分离,以及如何通过访客模式方便地增加新操作而不会影响已有的数据元素。同时,还提供了一个示例代码来帮助理解访客模式的工作原理。
997

被折叠的 条评论
为什么被折叠?



