旧游无处不堪寻
无寻处,惟有少年心
设计模式-访问者

访问者模式(Visitor Pattern)是一种行为型设计模式,它能将算法与其所作用的对象隔离开来。

解释


访问者模式建议将新行为放入一个名为访问者的独立类中,而不是试图将其整合到已有类中。现在,需要执行操作的原始对象将作为参数被传递给访问者中的方法,让方法能访问对象所包含的一切必要数据。

访问者模式 UML


适用场景


  1. 如果你需要对一个复杂对象结构(例如对象树)中的所有元素执行某些操作,可使用访问者模式
  2. 可使用访问者模式来清理辅助行为的业务逻辑
  3. 当某个行为仅在类层次结构中的一些类中有意义,而在其他类中没有意义时,可使用该模式

代码示例


using System;
namespace VisitorPatternDemo
{
// The Component interface declares an `accept` method that should take the
// base visitor interface as an argument.
public interface IComponent
{
void Accept(IVisitor visitor);
}

// Each Concrete Component must implement the `Accept` method in such a way
// that it calls the visitor's method corresponding to the component's
// class.
public class ConcreteComponentA : IComponent
{
// Note that we're calling `VisitConcreteComponentA`, which matches the
// current class name. This way we let the visitor know the class of the
// component it works with.
public void Accept(IVisitor visitor)
{
visitor.VisitConcreteComponentA(this);
}

// Concrete Components may have special methods that don't exist in
// their base class or interface. The Visitor is still able to use these
// methods since it's aware of the component's concrete class.
public string ExclusiveMethodOfConcreteComponentA()
{
return "A";
}
}

public class ConcreteComponentB : IComponent
{
// Same here: VisitConcreteComponentB => ConcreteComponentB
public void Accept(IVisitor visitor)
{
visitor.VisitConcreteComponentB(this);
}

public string SpecialMethodOfConcreteComponentB()
{
return "B";
}
}

// The Visitor Interface declares a set of visiting methods that correspond
// to component classes. The signature of a visiting method allows the
// visitor to identify the exact class of the component that it's dealing
// with.
public interface IVisitor
{
void VisitConcreteComponentA(ConcreteComponentA element);

void VisitConcreteComponentB(ConcreteComponentB element);
}

// Concrete Visitors implement several versions of the same algorithm, which
// can work with all concrete component classes.
//
// You can experience the biggest benefit of the Visitor pattern when using
// it with a complex object structure, such as a Composite tree. In this
// case, it might be helpful to store some intermediate state of the
// algorithm while executing visitor's methods over various objects of the
// structure.
class ConcreteVisitor1 : IVisitor
{
public void VisitConcreteComponentA(ConcreteComponentA element)
{
Console.WriteLine(element.ExclusiveMethodOfConcreteComponentA() + " + ConcreteVisitor1");
}

public void VisitConcreteComponentB(ConcreteComponentB element)
{
Console.WriteLine(element.SpecialMethodOfConcreteComponentB() + " + ConcreteVisitor1");
}
}

class ConcreteVisitor2 : IVisitor
{
public void VisitConcreteComponentA(ConcreteComponentA element)
{
Console.WriteLine(element.ExclusiveMethodOfConcreteComponentA() + " + ConcreteVisitor2");
}

public void VisitConcreteComponentB(ConcreteComponentB element)
{
Console.WriteLine(element.SpecialMethodOfConcreteComponentB() + " + ConcreteVisitor2");
}
}

public class Client
{
// The client code can run visitor operations over any set of elements
// without figuring out their concrete classes. The accept operation
// directs a call to the appropriate operation in the visitor object.
public static void ClientCode(List<IComponent> components, IVisitor visitor)
{
foreach (var component in components)
{
component.Accept(visitor);
}
}
}

class Program
{
static void Main(string[] args)
{
List<IComponent> components = new List<IComponent>
{
new ConcreteComponentA(),
new ConcreteComponentB()
};

Console.WriteLine("The client code works with all visitors via the base Visitor interface:");
var visitor1 = new ConcreteVisitor1();
Client.ClientCode(components,visitor1);

Console.WriteLine();

Console.WriteLine("It allows the same client code to work with different types of visitors:");
var visitor2 = new ConcreteVisitor2();
Client.ClientCode(components, visitor2);
// output

// The client code works with all visitors via the base Visitor interface:
// A + ConcreteVisitor1
// B + ConcreteVisitor1

// It allows the same client code to work with different types of visitors:
// A + ConcreteVisitor2
// B + ConcreteVisitor2
}
}
}