Tuesday, December 18, 2018

Visitor Pattern


Visitor design pattern lets you to define a new operation without changing or modifying its existing classes. Visitor Pattern falls under behavioral pattern of GOF (Gang of Four) patterns.

When to use –


Visitor pattern can use when we want to perform an operation on similar kind of objects. Using visitor pattern we can add new functionality to the original objects or hierarchy or objects via Visitor subclass.

Visitor pattern consist of main two methods. One method is Visit() which is called for every element in the object hierarchy. Second method is Accept() which is implemented by the class who is visiting object hierarchy and accepted by visitors.

Major components of Visitor pattern –


Visitor – This is an interface declares Visit operation.
Concrete Visitor – This class implements Visitor interface and implements visit operation.
Element – This is an interface declares the Accept operation which takes visitor as an argument.
Concrete Element – This class implements Element interface and implements Accept operation.
Client – Client is an entry point class that has access to data structure objects and process elements to visit object hierarchy.

Let’s have a look on below example of Visitor design pattern.

Code –
public enum Categories
{
    Food,
    Oil,
    Electronics,
    Cloths
}
//element
public interface IElement
{
    void Accept(IVisitor visitor);
}
//concrete element
public class Products : IElement
{
    public void Accept(IVisitor visitor)
    {
        Console.WriteLine("Name - {0}, Product Visited - {1}, Category - {2}",
            visitor.Name, visitor.ProductVisited, visitor.Category);
    }
}
//concrete element
public class Discounts : IElement
{
    public void Accept(IVisitor visitor)
    {
        double discount = 0;
        switch (visitor.Category)
        {
            case Categories.Food:
                discount = 10;
                break;
            case Categories.Electronics:
                discount = 20;
                break;
            case Categories.Cloths:
                discount = 15;
                break;
            default:
                discount = 5;
                break;
        }
        Console.WriteLine("Product Name - {0}, Price - {1}, Discount ({2}%) - {3}",
            visitor.ProductVisited, visitor.Price, discount, (visitor.Price * discount)/100);
    }
}
//visitor
public interface IVisitor
{
    string Name { get; set; }
    string ProductVisited { get; set; }
    double Price { get; set; }
    Categories Category { get; set; }
    void Visit(IElement element);
}
//concrete visitor
public class GroceriesVisitor : IVisitor
{
    public string Name { get; set; }
    public string ProductVisited { get; set; }
    public double Price { get ; set; }
    public Categories Category { get; set; }
    public GroceriesVisitor(string name, string productVisited, double price, Categories category )
    {
        Name = name;
        ProductVisited = productVisited;
        Price = price;
        Category = category;
    }
    public void Visit(IElement element)
    {
        element.Accept(this);
    }
}
//concrete visitor
public class AppliancesVisitor : IVisitor
{
    public string Name { get; set; }
    public string ProductVisited { get; set; }
    public double Price { get; set; }
    public Categories Category { get; set; }
    public AppliancesVisitor(string name, string productVisited, double price, Categories category)
    {
        Name = name;
        ProductVisited = productVisited;
        Price = price;
        Category = category;
    }
    public void Visit(IElement element)
    {
        element.Accept(this);
    }
}
//concrete visitor
public class FashionVisitor : IVisitor
{
    public string Name { get; set; }
    public string ProductVisited { get; set; }
    public double Price { get; set; }
    public Categories Category { get; set; }
    public FashionVisitor(string name, string productVisited, double price, Categories category)
    {
        Name = name;
        ProductVisited = productVisited;
        Price = price;
        Category = category;
    }
    public void Visit(IElement element)
    {
        element.Accept(this);
    }
}
//entry point
class Program
{
    static void Main(string[] args)
    {
        List<IVisitor> Items = new List<IVisitor>();

        Items.Add(new GroceriesVisitor("Visitor1", "Rice", 150, Categories.Food));
        Items.Add(new GroceriesVisitor("Visitor2", "Oil", 95, Categories.Oil));
        Items.Add(new AppliancesVisitor("Visitor3", "Speakers", 500, Categories.Electronics));
        Items.Add(new AppliancesVisitor("Visitor4", "Cameras", 500, Categories.Electronics));
        Items.Add(new FashionVisitor("Visitor5", "Shirts", 1500, Categories.Cloths));
        Items.Add(new FashionVisitor("Visitor6", "Jeans", 3000, Categories.Cloths));

        Console.WriteLine("-----Visiting prices of all items-----");
        foreach(var item in Items)
        {
            item.Visit(new Products());
        }
           
        Console.WriteLine("-----Visiting discounts of all items-----");
        foreach (var item in Items)
        {
            item.Visit(new Discounts());
        }

        Console.Read();
    }
}

Output –



As per above example, there are many visitor and elements defined. Different type of visitors visits various products. In this example Product object allows different visitor to visit product related information like Product Name, category etc. where Discount object allows different visitor to visit discounts available on various categories of products.

You can download full code from Gist.

I hope this article helps you to know more about Visitor Design Pattern. Please leave your feedback in comments section below.

References –