preface

Today we are going to look at Visitor Pattern. Understanding the name Visitor Pattern may help us understand its core code block. Let’s look at an example: I go to a friend’s house. The friend belongs to the host, and I belong to the visitor. At this time just friends in cooking, but no soy sauce. If a friend goes down to buy soy sauce, it will be troublesome and affect the cooking. This is where I, the visitor, come in. He went out and came back with soy sauce. To put it simply, the visitor helps the host to accomplish something inconvenient or impossible on the basis of the original host.

Introduction to the Visitor Pattern

A, the other

In software system development, we often encounter well-structured code that changes as requirements change. You say that at this point I will change its base class, all subclasses have to change, this is a very cumbersome thing. Can I make changes to new requirements without leaving the modifier hierarchy intact?

Second, the intention

Represents an operation that operates on elements in an object structure. It can define new operations on elements without changing their classes.

3. Case diagram

Code examples of visitor pattern

Looking at the example diagram above, you can see that the visitor pattern contains the following sections:

Struct object: a container of nodes, containing multiple classes or interfaces abstract node: declares a receive operation, receives a visitor object as a parameter, declares a processing interface, processes node data

Concrete node: Implement the receive and process operations of the abstract node

Abstract visitor: Declares one or more access operations that must be implemented by all concrete visitors

Concrete visitor: An interface that implements abstract visitor declarations

We look at such a requirement, we need to calculate the area of the graph and output, graph including rectangle circle. Let’s take a look at the code implementation:

First let’s see if we can implement the visitor pattern:

Namespace Visitor_Pattern {class VisitorPattern {} // <summary> /// abstract node class // </summary> public Abstract class Element { public abstract void CalculatedArea(); } // Rectangle = Rectangle; // Rectangle = Rectangle; // Rectangle = Rectangle; public double _wide; public Rectangle(double Long, double Wide) { this._long = Long; this._wide = Wide; } public override voidCalculatedArea()
        {
            Console.WriteLine($"The area of the rectangle is {_long*_wide}"); }} /// </summary> // </summary> public class: Element {public double _r; public Circular(double r) { this._r = r; } public override voidCalculatedArea()
        {
            Console.WriteLine($"The circular area is: {math.pi *_r *_r}"); }} /// </summary> public class Graphical {public List<Element> elements = new List<Element>(); public List<Element> Elements { get {return elements; }
        }

        public Graphical() {Rectangle = new Rectangle(10,5); Element element1= new Circular(5); elements.Add(element); elements.Add(element1); }}}Copy the code

namespace Visitor_Pattern
{
    class Program
    {
        static void Main(string[] args)
        {
            Graphical graphical = new Graphical();
            foreach (var item ingraphical.Elements) { item.CalculatedArea(); }}}}Copy the code

As you can see, the implementation is relatively simple. But if the requirements change here, you need to add the perimeter of the graph and print and print its parameters. That’s the trouble. You need to make changes to the base class and then modify the subclasses. It feels like a little too much to lose.

Let’s look at pattern implementation:

namespace Visitor_Pattern
{
    class UseVisitorPattern
    {
    }

    Visitors to the # region/// </summary> public interface IVistor {void Visit(UseRectangle rectangle); void Visit(UseCircular useCircular); } /// </summary> // </summary> public class Vistor: IVistor { public void Visit(UseRectangle rectangle) { rectangle.CalculatedArea(); Console.WriteLine($"Rectangle length is: {rectangle._long}");
            Console.WriteLine($"Rectangle width: {rectangle._wide}");
            Console.WriteLine($"Rectangle circumference is: {2*(rectangle._long+rectangle._wide)}");
        }

        public void Visit(UseCircular useCircular)
        {
            useCircular.CalculatedArea();
            Console.WriteLine($"The radius of the circle is {useCircular._r}");
            Console.WriteLine($"{2* math.pi *useCircular._r}"); }}#endregion

    # region node class/// </summary> public abstract class UseElement {public abstract void Accept(IVistor vistor); public abstract void CalculatedArea(); } /// </summary> public class UseRectangle: UseElement {public double _long; public double _wide; public UseRectangle(double Long, double Wide) { this._long = Long; this._wide = Wide; } public override void Accept(IVistor vistor) { vistor.Visit(this); } public override voidCalculatedArea()
        {
            Console.WriteLine($"The area of the rectangle is {_long * _wide}"); {// </summary> public class UseCircular: UseElement {public double; public UseCircular(double r) { this._r = r; } public override void Accept(IVistor vistor) { vistor.Visit(this); } public override voidCalculatedArea()
        {
            Console.WriteLine($"The circular area is: {math.pi * _r * _r}"); }}#endregion// </summary> public class UseGraphical {public List<UseElement> elements = new List<UseElement>(); public List<UseElement> Elements { get {return elements; }
        }

        public UseGraphical() { UseElement element = new UseRectangle(10, 5); UseElement element1 = new UseCircular(5); elements.Add(element); elements.Add(element1); }}}Copy the code

namespace Visitor_Pattern
{
    class Program
    {
        static void Main(string[] args)
        {

            UseGraphical graphical = new UseGraphical();
            foreach (var item ingraphical.Elements) { item.Accept(new Vistor()); }}}}Copy the code

Here we add visitors to each node, so that we can modify and add specific visitors when we need to change the perimeter and parameter output.

Usage scenarios and advantages and disadvantages

First, use scenarios

1. The corresponding classes of objects in the object structure change less, but new operations are often defined on this object structure

2. You need to perform some unrelated operations on an object structure, and you need to avoid changing its original class when adding operations

Second, the advantages of

1. Conform to the principle of single responsibility. Each class is responsible for one responsibility

2. Excellent scalability and flexibility, it is easier to add new operations without changing its original structural code

3. The visitor pattern collects related actions in the visitor object rather than scattering them in its element class

Three and disadvantages

1. Specific elements expose details to visitors, violating Demeter’s principle

2. It becomes difficult to add specific element nodes, and as a result, add new ones to visitors.

conclusion

So much for the visitor pattern. The visitor pattern focuses on separating data structures from operations, addressing the coupling between stable data structures and easily changeable operations. New operations do not modify the class of the original structure object. To add an action, modify the action in the visitor object directly.

If we want more roses, we must plant more trees.