1 8.9K ru

Паттерн «Посетитель» (Visitor)

Categories: 💻 Programming

Назначение: описывает операцию, выполняемую с каждым объектом из некоторой иерархии классов. Паттерн «Посетитель» позволяет определить новую операцию, не изменяя классов этих объектов.

Когда использовать паттерн Посетитель

  • Паттерн Посетитель определяет операцию, выполняемую на каждом элементе из некоторой структуры. Позволяет, не изменяя классы этих объектов, добавлять в них новые операции.
  • Является классической техникой для восстановления потерянной информации о типе.
  • Паттерн Посетитель позволяет выполнить нужные действия в зависимости от типов двух объектов.
  • Предоставляет механизм двойной диспетчеризации.

UML схема паттерна Visitor

uml schema visitor

 

Реализация шаблона "посетитель" на C#

using System;
using System.Collections.Generic;
 
namespace DoFactory.GangOfFour.Visitor.Structural
{
  /// <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);
      }
    }
  }
}

Схема примера использования паттерна "Посетитель"  из реальной жизни:

Пример шаблона посетитель с реальной жизни

Примеры в .NET Framework 

  • ExpressionTreeVisitor используется для работы с деревьями выражений (Expression Trees) в .NET Framework. Данный посетитель используется для навигации и преобразования деревьев выражений при реализации специализированных LINQ-провайдеров, а также для решения других задач. 
  • Roslyn содержит множество посетителей. CSharpSyntaxVisitor предназначен для работы с синтаксическим деревом, SymbolVisitor<TResult> — для работы с символами и др.
  • DbExpressionVisitor используется в Entity Framework для SQL-выражений.

Comments:

Please log in to be able add comments.
👍