The Visitor Pattern is a behavioral design pattern that allows you to add further operations to objects without having to modify them. It is particularly useful when you have a structure of objects and you want to perform operations on these objects that can be defined outside of their classes. This pattern follows the Open/Closed Principle by allowing new functionality to be added without altering the existing code.
The Visitor Design Pattern should be used when you have distinct and unrelated operations to perform across a structure of objects (element objects). That means the Visitor Design is used to create and perform new operations on a set of objects without changing the object structure or classes.
Components of Visitor Pattern
- Visitor Interface: Declares a Visit method for each type of ConcreteElement in the object structure.
- ConcreteVisitor: Implements the Visitor interface and defines the actions for each type of ConcreteElement.
- Element Interface: Declares an Accept method that takes a Visitor as an argument.
- ConcreteElement: Implements the Element interface and defines the Accept method to call the Visitor's method.
- Object Structure: Can be a collection or a composite of elements which can be iterated to apply the Visitor.
Example in C#
Let's say we have a set of shapes (Circle, Rectangle, Triangle) and we want to calculate their area and perimeter. Using the Visitor Pattern, we can define the operations (calculating area and perimeter) outside of the shape classes.
Define the Visitor Interfaceusing VisitorPattern.Element; namespace VisitorPattern.Visitor { /// <summary> /// Visitor Interface /// </summary> public interface IVisitor { void Visit(Circle circle); void Visit(Rectangle rectangle); void Visit(Triangle triangle); } }
using VisitorPattern.Visitor; namespace VisitorPattern.Element { /// <summary> /// Element Interface /// </summary> public interface IShape { void Accept(IVisitor visitor); } }
// Circle.cs using VisitorPattern.Visitor; namespace VisitorPattern.Element { /// <summary> /// Concrete Element1 /// </summary> public class Circle : IShape { public double Radius { get; set; } public Circle(double radius) { Radius = radius; } public void Accept(IVisitor visitor) { visitor.Visit(this); } } } // Rectangle.cs using VisitorPattern.Visitor; namespace VisitorPattern.Element { /// <summary> /// Concrete Element2 /// </summary> public class Rectangle : IShape { public double Width { get; set; } public double Height { get; set; } public Rectangle(double width, double height) { Width = width; Height = height; } public void Accept(IVisitor visitor) { visitor.Visit(this); } } } // Triangle.cs using VisitorPattern.Visitor; namespace VisitorPattern.Element { /// <summary> /// Concrete Element3 /// </summary> public class Triangle : IShape { public double Base { get; set; } public double Height { get; set; } public Triangle(double @base, double height) { Base = @base; Height = height; } public void Accept(IVisitor visitor) { visitor.Visit(this); } } }
// AreaVisitor.cs using VisitorPattern.Element; namespace VisitorPattern.Visitor { /// <summary> /// Concrete Visitor1 /// </summary> public class AreaVisitor : IVisitor { public double TotalArea { get; private set; } public void Visit(Circle circle) { TotalArea += Math.PI * circle.Radius * circle.Radius; } public void Visit(Rectangle rectangle) { TotalArea += rectangle.Width * rectangle.Height; } public void Visit(Triangle triangle) { TotalArea += 0.5 * triangle.Base * triangle.Height; } } } // PerimeterVisitor.cs using VisitorPattern.Element; namespace VisitorPattern.Visitor { /// <summary> /// Concrete Visitor2 /// </summary> public class PerimeterVisitor : IVisitor { public double TotalPerimeter { get; private set; } public void Visit(Circle circle) { TotalPerimeter += 2 * Math.PI * circle.Radius; } public void Visit(Rectangle rectangle) { TotalPerimeter += 2 * (rectangle.Width + rectangle.Height); } public void Visit(Triangle triangle) { // Assuming it's an equilateral triangle for simplicity TotalPerimeter += 3 * triangle.Base; } } }
using VisitorPattern.Element; using VisitorPattern.Visitor; List<IShape> shapes = new List<IShape> { new Circle(5), new Rectangle(4, 6), new Triangle(3, 4) }; AreaVisitor areaVisitor = new AreaVisitor(); PerimeterVisitor perimeterVisitor = new PerimeterVisitor(); foreach (var shape in shapes) { shape.Accept(areaVisitor); shape.Accept(perimeterVisitor); } Console.WriteLine($"Total Area: {areaVisitor.TotalArea}"); Console.WriteLine($"Total Perimeter: {perimeterVisitor.TotalPerimeter}"); Console.ReadLine();
Output
Explanation
- Visitor Interface (IVisitor): Declares Visit methods for each type of shape.
- Concrete Visitors (AreaVisitor, PerimeterVisitor): Implement the IVisitor interface and provide the logic for calculating area and perimeter.
- Element Interface (IShape): Declares the Accept method.
- Concrete Elements (Circle, Rectangle, Triangle): Implement the IShape interface and the Accept method to call the appropriate Visit method on the visitor.
- Object Structure: A list of shapes that can be iterated to apply the visitors.
The full source code is available here:
Happy coding!! 😊
No comments:
Post a Comment