Design Patterns

Last Updated: 10/3/2024

Visitor

  • Allows to add new operation to an object structure without modifying it
  • Use this approach when object structure is stable, and you want to support new operation

Classical Structure

Visitor Classical Structure

Scenario

  • Build an html editor

Problem

  • Violates Open Closed Principle
  • Logic for each operation is spread across different classes
internal interface IHtmlNode
{
    void Highlight();
}
internal class HeadingNode: IHtmlNode
{
    public void Highlight()
    {
        Console.WriteLine("Highlight heading");
    }
}
internal class AnchorNode : IHtmlNode
{
    public void Highlight()
    {
        Console.WriteLine("Highlight anchor");
    }
}
internal class HtmlDocument
{
    List<IHtmlNode> nodes = new List<IHtmlNode>();

    public void Add(IHtmlNode node)
    {
        nodes.Add(node);
    }

    public void Highlight()
    {
        foreach (IHtmlNode node in nodes)
        {
            node.Highlight();
        }
    }
}

Solution


internal interface IOperation
{
    void Apply(HeadingNode node);
    void Apply(AnchorNode node);
}
internal interface IHtmlNode
{
    void Apply(IOperation operation);
}
internal class HeadingNode: IHtmlNode
{
    public void Apply(IOperation operation)
    {
        operation.Apply(this);
    }
}
internal class AnchorNode : IHtmlNode
{
    public void Apply(IOperation operation)
    {
        operation.Apply(this);
    }
}
internal class HeadingNode: IHtmlNode
{
    public void Apply(IOperation operation)
    {
        operation.Apply(this);
    }
}
internal class HtmlDocument
{
    List<IHtmlNode> nodes = new List<IHtmlNode>();

    public void Add(IHtmlNode node)
    {
        nodes.Add(node);
    }

    public void Apply(IOperation operation)
    {
        foreach (IHtmlNode node in nodes)
        {
            node.Apply(operation);
        }
    }
}
internal class HighlightOperation : IOperation
{
    public void Apply(HeadingNode node)
    {
        Console.WriteLine("Highlight heading");
    }

    public void Apply(AnchorNode node)
    {
        Console.WriteLine("Highlight anchor");
    }
}
internal class PlainText: IOperation
{
    public void Apply(HeadingNode node)
    {
        Console.WriteLine("PlaintText heading");
    }

    public void Apply(AnchorNode node)
    {
        Console.WriteLine("PlaintText anchor");
    }
}
internal class HtmlDocument
{
    List<IHtmlNode> nodes = new List<IHtmlNode>();

    public void Add(IHtmlNode node)
    {
        nodes.Add(node);
    }

    public void Apply(IOperation operation)
    {
        foreach (IHtmlNode node in nodes)
        {
            node.Apply(operation);
        }
    }
}

public class Program
{
    static void Main(string[] args)
    {
        var document = new HtmlDocument();
        document.Add(new HeadingNode());
        document.Add(new AnchorNode());
        document.Apply(new HighlightOperation());
        document.Apply(new PlainText());
    }
}

Example Structure

Visitor Example Structure

Example Code