Visitor - Design Pattern

Visitor - Design Pattern

Software Architecture Simplified

·

3 min read

Objective 🎯

Provide a way of separating an algorithm from an object allowing to add/change operations at run time.

Type ✅

✔️Behavioral: Describes how objects interact/communicate between themselves.

❌Creational: Describes how to instantiate an object without large and complex.

❌Structural: Describes how objects/classes are composed to form larger structures.

UML 📐

image.png

Participants 🔗

• Visitor:

  • Declares a specific method (Visit) for each ConcreteElement in the Object Structure

• ConcreteVisitor:

  • Implements each operation declared in the Visitor

• Element:

  • Defines an Accept method that receives a Visitor as an argument

• ConcreteElement:

  • Implements the Element's interface

• ObjectStructure:

  • Maintains a list of elements
  • Provides an interface to attach and remove elements in the list

Sample Code 🎮

Structural Example 🏛️

image.png

namespace Main.Visitor
{
    public static class VisitorStructural
    {
        public static void Execute()
        {
            ObjectStructure lObjectStructure = new ObjectStructure();
            lObjectStructure.Attach(new ConcreteElementA());
            lObjectStructure.Attach(new ConcreteElementB());

            ConcreteVisitor1 lConcreteVisitor1 = new ConcreteVisitor1();
            ConcreteVisitor2 lConcreteVisitor2 = new ConcreteVisitor2();

            lObjectStructure.Accept(lConcreteVisitor1);
            lObjectStructure.Accept(lConcreteVisitor2);
        }
    }

    public abstract class Visitor
    {
        public abstract void VisitConcreteElementA(ConcreteElementA prConcreteElementA);
        public abstract void VisitConcreteElementB(ConcreteElementB prConcreteElementB);
    }

    public class ConcreteVisitor1 : Visitor
    {
        public override void VisitConcreteElementA(ConcreteElementA prConcreteElementA)
        {
            Console.WriteLine("{0} visited by {1}",
                prConcreteElementA.GetType().Name, this.GetType().Name);
        }

        public override void VisitConcreteElementB(ConcreteElementB prConcreteElementB)
        {
            Console.WriteLine("{0} visited by {1}",
                prConcreteElementB.GetType().Name, this.GetType().Name);
        }
    }

    public class ConcreteVisitor2 : Visitor
    {
        public override void VisitConcreteElementA(ConcreteElementA prConcreteElementA)
        {
            Console.WriteLine("{0} visited by {1}", prConcreteElementA.GetType().Name, this.GetType().Name);
        }

        public override void VisitConcreteElementB(ConcreteElementB prConcreteElementB)
        {
            Console.WriteLine("{0} visited by {1}", prConcreteElementB.GetType().Name, this.GetType().Name);
        }
    }

    public abstract class Element
    {
        public abstract void Accept(Visitor prVisitor);
    }

    public class ConcreteElementA : Element
    {
        public override void Accept(Visitor prVisitor)
        {
            prVisitor.VisitConcreteElementA(this);
        }
    }

    public class ConcreteElementB : Element
    {
        public override void Accept(Visitor prVisitor)
        {
            prVisitor.VisitConcreteElementB(this);
        }
    }

    public class ObjectStructure
    {
        private List<Element> _Elements = new List<Element>();

        public void Attach(Element prElement)
        {
            _Elements.Add(prElement);
        }

        public void Detach(Element prElement)
        {
            _Elements.Remove(prElement);
        }

        public void Accept(Visitor prVisitor)
        {
            foreach (Element lElementCurrent in _Elements)
            {
                lElementCurrent.Accept(prVisitor);
            }
        }
    }
}

Output

image.png

Real-world Example 🔥

image.png

public static class VisitorPractical
    {
        public static void Execute()
        {
            School lSchool = new School();
            lSchool.Attach(new Student() { _Name = "Frank" });
            lSchool.Attach(new Student() { _Name = "Matt" });
            lSchool.Attach(new Student() { _Name = "Jennifer" });

            lSchool.Accept(new Doctor());
            lSchool.Accept(new CarrerCoach());
        }
    }

    public interface IVisitor
    {
        public void Visit(Student prStudent);
    }

    public interface IElement
    {
        public void Accept(IVisitor prVisitor);
    }

    public class Student : IElement
    {
        public string _Name { get; set; }
        public void Accept(IVisitor prVisitor)
        {
            prVisitor.Visit(this);
        }
    }

    public class Doctor : IVisitor
    {
        public void Visit(Student prStudent)
        {
            Console.WriteLine($"Student \"{prStudent._Name}\" visited by {this.GetType().Name}");
        }
    }

    public class CarrerCoach : IVisitor
    {
        public void Visit(Student prStudent)
        {
            Console.WriteLine($"Student \"{prStudent._Name}\" visited by {this.GetType().Name}");
        }
    }

    public class School
    {
        private List<Student> _Students = new List<Student>();

        public void Attach(Student prStudent)
        {
            _Students.Add(prStudent);
        }

        public void Detach(Student prStudent)
        {
            _Students.Remove(prStudent);
        }

        public void Accept(IVisitor prVisitor)
        {
            foreach (Student lStudentCurrent in _Students)
            {
                lStudentCurrent.Accept(prVisitor);
            }
        }
    }

Output

image.png

Source Code 🎲

github.com/VictorLins/DesignPatterns

Did you find this article valuable?

Support Victor Lins by becoming a sponsor. Any amount is appreciated!