© Vaskaran Sarcar 2020
V. SarcarDesign Patterns in C#https://doi.org/10.1007/978-1-4842-6062-3_13

13. Visitor Pattern

Vaskaran Sarcar1 
(1)
Garia, Kolkata, West Bengal, India
 

This chapter covers the Visitor pattern.

GoF Definition

Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

Concept

In this pattern, you separate an algorithm from an object structure. So, you can add new operations on objects without modifying their existing architecture. This pattern supports the open/close principle (which says the extension is allowed, but modification is disallowed for entities such as class, function, and so on).

Note

You can experience the true power of this design pattern when you combine it with the Composite pattern, as shown in an implementation later in this chapter.

To understand this pattern, let’s consider a scenario in which you have an abstract class called Number as follows.
    /// <summary>
    /// Abstract class- Number
    /// </summary>
    abstract class Number
    {
        private int numberValue;
        private string type;
        public Number(string type, int number)
        {
            this.type = type;
            this.numberValue = number;
        }
        // I want to restrict the change in original data
        // So, no setter is present here.
        public int NumberValue
        {
            get
            {
                return numberValue;
            }
        }
        public string TypeInfo
        {
            get
            {
                return type;
            }
        }
        public abstract void SomeMethod();
    }
There are two concrete classes called SmallNumber and BigNumber that derive from Number, which is defined as follows.
    /// <summary>
    /// Concrete class-SmallNumber
    /// </summary>
    class SmallNumber : Number
    {
        public SmallNumber(string type, int number) : base(type, number)
        { }
        public override void SomeMethod()
        {
            // Some code
        }
    }
    /// <summary>
    /// Concrete class-BigNumber
    /// </summary>
    class BigNumber : Number
    {
        public BigNumber(string type, int number) : base(type, number)
        { }
        public override void SomeMethod
        {
            // Some code
        }
    }

This inheritance hierarchy is easy to understand. Now let’s look at an imaginary conversation between you and your customer.

Customer: I want you to create a design in which each concrete class has a method to increment the number value.

You: That’s easy. I’ll introduce a common method in the Number class, and as a result, each of the concrete classes can get the method.

Customer: Wait. I want you to use a method that increments the number, but in each invocation of the method in the SmallNumber class, it should increment the number by 1, and for the BigNumber class, it should increment the number by 10.

You: That won’t be a problem. I can define an abstract method in the Number class, and in each of the derived classes, you can implement it differently.

Customer: That’s fine with me.

You can accept this customer request as a one-off, but if your client often asks for similar requests, will it be possible for you to introduce methods like this in each class, particularly when the overall code structure is very complex? Also, in a tree structure, if it is just a branch node, can you imagine the impact of these changes across other nodes?

This time you may understand the problem and may think of some way to handle your fickle-minded customers. The Visitor pattern can help you in a situation like this. You see such an implementation in demonstration 1.

Real-World Example

Think of a taxi-booking scenario. When the taxi arrives at your door, and you enter the vehicle, the taxi driver takes control of transportation. He can take you to your destination through a route that you are not familiar with, and in the worst case, it can alter the destination (which is generated due to improper use of the Visitor pattern).

Computer-World Example

This pattern is useful when public APIs need to support plug-in operations. Clients can then perform their intended operations on a class (with the visiting class) without modifying the source.

Implementation

Let’s continue our discussion on the Visitor pattern. You can see the class diagram in Figure 13-1. It gives you a hint on how I’ve implemented it in the upcoming demonstration. I introduced a new hierarchy, in which, at the top level, there is an interface called IVisitor with two methods called VisitBigNumbers(..) and VisitSmallNumbers(..) . It looks like the following.
    interface IVisitor
    {
        // A visit operation for SmallNumber class
        void VisitSmallNumbers(SmallNumber number);
        // A visit operation for BigNumber class
        void VisitBigNumbers(BigNumber number);
    }
Note

Instead of using different names (VisitSmallNumbers(..), VisitBigNumbers(...)) for these methods, you could use the same method (for example, VisitNumbers(...)) by using method overloading. In the Q&A session, I discuss the reason for using different names in this example.

IncrementNumberVisitor implements this interface method, which looks like the following.
    class IncrementNumberVisitor : IVisitor
    {
        public void VisitSmallNumbers(SmallNumber number)
        {
            Number currentNumber = number as Number;
            /*
             I do not want (infact I can't change because it's readonly now) to modify the original data. So, I'm making a copy of it before I use it.
            */
            int temp = currentNumber.NumberValue;
            // For SmallNumber's incrementing by 1
            Console.WriteLine($"{currentNumber.TypeInfo} is {currentNumber.NumberValue}; I use it as:{++temp} for rest of my code.");
            // Remaining code, if any
        }
        public void VisitBigNumbers(BigNumber number)
        {
            Number currentNumber = number as Number;
            /*
              * I do not want (infact I can't change because it's readonly now)
             * to modify the original data.
             * So, I'm making a copy of it before I use it.
             */
            int temp = currentNumber.NumberValue;
            // For BigNumber's incrementing by 10
            Console.WriteLine($"{currentNumber.TypeInfo} is {currentNumber.NumberValue}; I convert it as:{temp+10} for rest of my code.");
            // Remaining code, if any
        }
    }

One interesting point to note is that I do not want to modify the original data. So, in the Number class, you see the getter methods only. It is because I assume that once you get the data from the concrete Number classes, you can use it differently, but you are not allowed to change the original data. (It’s a better practice, but it’s optional).

In this example, I maintain a List data structure, called numberList , which initializes an object structure with different types of numbers. So, in demonstration 1, you get the following code segment.
    class NumberCollection
    {
        List<Number> numberList = new List<Number>();
        // List contains both SmallNumber's and BigNumber's
        public NumberCollection()
        {
            numberList.Add(new SmallNumber("small-1", 10));
            numberList.Add(new SmallNumber("small-2", 20));
            numberList.Add(new SmallNumber("small-3", 30));
            numberList.Add(new BigNumber("big-1", 200));
            numberList.Add(new BigNumber("big-2", 150));
            numberList.Add(new BigNumber("big-3", 70));
        }
            // remaining code
Again, you can initialize the list in this way, or once you initialize an empty list, you can supply the elements of the lists inside the client code using the AddNumberToList(...) method. Similarly, you can remove an element from your list using the RemoveNumberFromList(...) method . In demonstration 1, I did not use these methods, but I kept them for your reference. So, note the following methods.
        public void AddNumberToList(Number number)
        {
            numberList.Add(number);
        }
        public void RemoveNumberFromList(Number number)
        {
            numberList.Remove(number);
        }
Now we come to the most important segment. Inside the Number class, you see the following line.
public abstract void Accept(IVisitor visitor);
The concrete derived classes from Number override it as needed. For example, SmallNumber overrides it as follows.
      public override void Accept(IVisitor visitor)
      {
        visitor.VisitSmallNumbers(this);
      }
And BigNumber implements it as follows.
      public override void Accept(IVisitor visitor)
      {
        visitor.VisitBigNumbers(this);
      }
You can see that inside the Accept method, you can pass a “particular visitor object,” which in turn can call the appropriate method across the classes. Both the SmallNumber and BigNumber classes expose themselves through this method (and here encapsulation is compromised). Now the client interacts with the visitor, and you can add new methods in the Visitor hierarchy. So, inside the client code, you notice code segments like the following.
NumberCollection numberCollection = new NumberCollection();
// some other code
// ....
IncrementNumberVisitor incrVisitor = new IncrementNumberVisitor();
// Visitor is visiting the list
Console.WriteLine("IncrementNumberVisitor is about to visit the list:");
numberCollection.Accept(incrVisitor);

Class Diagram

Figure 13-1 shows the class diagram. This time I wanted you to show the full method signature in the class diagram, so, to accommodate everything in a common place, the participants size become smaller than usual.
../images/463942_2_En_13_Chapter/463942_2_En_13_Fig1_HTML.jpg
Figure 13-1

Class diagram

Solution Explorer View

Figure 13-2 shows the high-level structure of the program.
../images/463942_2_En_13_Chapter/463942_2_En_13_Fig2_HTML.jpg
Figure 13-2

Solution Explorer view

Demonstration 1

Here’s the complete code.
using System;
using System.Collections.Generic;
namespace VisitorPattern
{
    /// <summary>
    /// Abstract class- Number
    /// </summary>
    abstract class Number
    {
        private int numberValue;
        private string type;
        public Number(string type, int number)
        {
            this.type = type;
            this.numberValue = number;
        }
        //I want to restrict the change in original data
        //So, no setter is present here.
        public int NumberValue
        {
            get
            {
                return numberValue;
            }
        }
        public string TypeInfo
        {
            get
            {
                return type;
            }
        }
        public abstract void Accept(IVisitor visitor);
    }
    /// <summary>
    /// Concrete class-SmallNumber
    /// </summary>
    class SmallNumber : Number
    {
        public SmallNumber(string type, int number) : base(type, number)
        { }
        public override void Accept(IVisitor visitor)
        {
            visitor.VisitSmallNumbers(this);
        }
    }
    /// <summary>
    /// Concrete class-BigNumber
    /// </summary>
    class BigNumber : Number
    {
        public BigNumber(string type, int number) : base(type, number)
        { }
        public override void Accept(IVisitor visitor)
        {
            visitor.VisitBigNumbers(this);
        }
    }
    class NumberCollection
    {
        List<Number> numberList = new List<Number>();
        //List contains both SmallNumber's and BigNumber's
        public NumberCollection()
        {
            numberList.Add(new SmallNumber("small-1", 10));
            numberList.Add(new SmallNumber("small-2", 20));
            numberList.Add(new SmallNumber("small-3", 30));
            numberList.Add(new BigNumber("big-1", 200));
            numberList.Add(new BigNumber("big-2", 150));
            numberList.Add(new BigNumber("big-3", 70));
        }
        public void AddNumberToList(Number number)
        {
            numberList.Add(number);
        }
        public void RemoveNumberFromList(Number number)
        {
            numberList.Remove(number);
        }
        public void DisplayList()
        {
            Console.WriteLine("Current list is as follows:");
            foreach (Number number in numberList)
            {
                Console.Write(number.NumberValue+" ");
            }
            Console.WriteLine();
        }
        public void Accept(IVisitor visitor)
        {
            foreach (Number n in numberList)
            {
                n.Accept(visitor);
            }
        }
    }
    /// <summary>
    /// The Visitor interface.
    /// GoF suggests to make visit opearation for each concrete class of /// ConcreteElement (in our example,SmallNumber and BigNumber) in the /// object structure
    /// </summary>
    interface IVisitor
    {
        //A visit operation for SmallNumber class
        void VisitSmallNumbers(SmallNumber number);
        //A visit operation for BigNumber class
        void VisitBigNumbers(BigNumber number);
    }
    /// <summary>
    /// A concrete visitor-IncrementNumberVisitor
    /// </summary>
    class IncrementNumberVisitor : IVisitor
    {
         public void VisitSmallNumbers(SmallNumber number)
        {
            Number currentNumber = number as Number;
            /*
             I do not want( infact I can't change because it's readonly now) to modify the original data. So, I'm making a copy of it before I use it.
            */
            int temp = currentNumber.NumberValue;
            //For SmallNumber's incrementing by 1
            Console.WriteLine($"{currentNumber.TypeInfo} is {currentNumber.NumberValue}; I use it as:{++temp} for rest of my code.");
            //Remaining code, if any
        }
        public void VisitBigNumbers(BigNumber number)
        {
            Number currentNumber = number as Number;
            /*
             I do not want( infact I can't change because it's readonly now) to modify the original data. So, I'm making a copy of it before I use it.
            */
            int temp = currentNumber.NumberValue;
            //For BigNumber's incrementing by 10
            Console.WriteLine($"{currentNumber.TypeInfo} is {currentNumber.NumberValue}; I convert it as:{temp+10} for rest of my code.");
            //Remaining code, if any
        }
    }
    class Client
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Visitor Pattern Demo*** ");
            NumberCollection numberCollection = new NumberCollection();
            //Showing the current list
            numberCollection.DisplayList();
            IncrementNumberVisitor incrVisitor = new IncrementNumberVisitor();
            //Visitor is visiting the list
            Console.WriteLine("IncrementNumberVisitor is about to visit the list:");
            numberCollection.Accept(incrVisitor);
            //Showing the current list
            numberCollection.DisplayList();
            Console.ReadLine();
        }
    }
}

Output

Here’s the output.
***Visitor Pattern Demo***
Current list is as follows:
10      20      30      200     150     70
IncrementNumberVisitor is about to visit the list:
small-1 is 10; I use it as:11 for rest of my code.
small-2 is 20; I use it as:21 for rest of my code.
small-3 is 30; I use it as:31 for rest of my code.
big-1 is 200; I convert it as:210 for rest of my code.
big-2 is 150; I convert it as:160 for rest of my code.
big-3 is 70; I convert it as:80 for rest of my code.
Current list is as follows:
10      20      30      200     150     70

Q&A Session

13.1 When should you consider implementing a Visitor design pattern?

Here are some use cases to consider.
  • You need to add new operations to a set of objects without changing their corresponding classes. It is the primary aim to implement a Visitor pattern. When the operations change very often, this approach can be your savior.

  • If you need to change the logic of various operations, you can simply do it through a visitor implementation.

13.2 Are there any drawbacks associated with this pattern?

Here are some drawbacks associated with this pattern.
  • I mentioned earlier that encapsulation is not its key concern. So, you can break the power of encapsulation using visitors.

  • If you need to frequently add new concrete classes to existing architecture, the visitor hierarchy becomes difficult to maintain. For example, suppose that you want to add another concrete class in the original hierarchy. In this case, you need to modify the visitor class hierarchy accordingly.

13.3 Why are you saying that a visitor class can violate the encapsulation ?

Notice that inside the Accept method, you can pass a “particular visitor object,” which in turn can call the appropriate method across the classes. Both SmallNumber and BigNumber class expose themselves through this method, and here encapsulation is compromised.

Also, in many cases, you may see that the visitor needs to move around a composite structure to gather information from them, and then it can modify with that information. (Though in demonstration 1, I do not allow this modification). So, when you provide this kind of support, you violate the core aim of encapsulation.

13.4 Why this pattern compromises with the encapsulation?

Here you perform some operations on a set of objects that can be heterogeneous also. But your constraint is that you cannot change their corresponding classes. So, your visitor needs a way to access the members of these objects. To fulfill this requirement, you are exposing the information to the visitor.

13.5 In demonstration 1, I see that in the visitor interfaces, you are not using the concept of method overloading . For example, you have written interface methods as follows.
        // A visit operation for SmallNumber class
        void VisitSmallNumbers(SmallNumber number);
        // A visit operation for BigNumber class
        void VisitBigNumbers(BigNumber number);
It appears to me that you could use something like the following.
        // A visit operation for SmallNumber class
        void VisitNumbers(SmallNumber number);
        // A visit operation for BigNumber class
        void VisitNumbers(BigNumber number);

Is this correct?

Nice catch. Yes, you can do that, but I wanted to draw your attention to the fact that these methods are doing different jobs (one is incrementing the int by 1 and the other is incrementing it by 10). By using different names, I tried to distinguish them inside the Number class hierarchy when you go through the code.

In the book Java Design Patterns (Apress, 2018), I used the approach that you mentioned. You can simply remember that these interface methods should target specific classes like SmallNumber or BigNumber only.

In demonstration 2, in which I combine the Visitor pattern with the Composite pattern, the overloaded methods are used.

13.6 Suppose that in demonstration 1, I added another concrete subclass of Number called UndefinedNumber . How should I proceed? Should I use another specific method in the visitor interface?

Exactly. You need to define a new method that is specific to this new class. So, your interface may look like the following (method overloading is used here).
   interface IVisitor
    {
        // A visit operation for SmallNumber class
        void VisitNumbers(SmallNumber number);
        // A visit operation for BigNumber class
        void VisitNumbers(BigNumber number);
        // A visit operation for UndefinedNumber class
        void VisitNumbers(UndefinedNumber number);
    }

And later, you need to implement this new method in the concrete visitor class.

13.7 Suppose, I need to support new operations in the existing architecture. How should I proceed with a Visitor pattern?

For each new operation, create a new subclass of Visitor and implement the operation in it. Then, visit your existing structure the way that I showed you in the preceding examples. For example, if you want methods that investigate whether the int values of SmallNumber class instances are greater than 10, and for the BigNumber class, whether they are greater than 100. For this requirement, you can add a new concrete class, InvestigateNumberVisitor, which inherits from IVisitor and is defined as follows.
    /// <summary>
    /// Another concrete visitor-InvestigateNumberVisitor
    /// </summary>
    class InvestigateNumberVisitor : IVisitor
    {
        public void VisitSmallNumbers(SmallNumber number)
        {
            Number currentNumber = number as Number;
            int temp = currentNumber.NumberValue;
            // Checking whether the number is greater than 10 or not
            string isTrue = temp > 10 ? "Yes" : "No";
            Console.WriteLine($"Is {currentNumber.TypeInfo} greater than 10 ? {isTrue}");
        }
        public void VisitBigNumbers(BigNumber number)
        {
            Number currentNumber = number as Number;
            int temp = currentNumber.NumberValue;
            // Checking whether the number is greater than 100 or not
            string isTrue = temp > 100 ? "Yes" : "No";
            Console.WriteLine($"Is {currentNumber.TypeInfo} greater than 100 ? {isTrue}");
        }
    }
Now inside the client code, you can add the following segment to check whether it is working properly or not.
// Visitor-2
InvestigateNumberVisitor investigateVisitor = new InvestigateNumberVisitor();
// Visitor is visiting the list
Console.WriteLine("InvestigateNumberVisitor is about to visit the list:");
numberCollection.Accept(investigateVisitor);
Once you add these segments in demonstration 1, use the client code as follows.
  class Client
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Visitor Pattern Demo2.*** ");
            NumberCollection numberCollection = new NumberCollection();
            // Showing the current list
            numberCollection.DisplayList();
            // Visitor-1
            IncrementNumberVisitor incrVisitor = new IncrementNumberVisitor();
            // Visitor is visiting the list
            Console.WriteLine("IncrementNumberVisitor is about to visit the list:");
            numberCollection.Accept(incrVisitor);
            // Visitor-2
            InvestigateNumberVisitor investigateVisitor = new InvestigateNumberVisitor();
            // Visitor is visiting the list
            Console.WriteLine("InvestigateNumberVisitor is about to visit the list:");
            numberCollection.Accept(investigateVisitor);
            Console.ReadLine();
        }
    }
You can get the following output when you run the program.
***Visitor Pattern Demo2.***
Current list is as follows:
10      20      30      200     150     70
IncrementNumberVisitor is about to visit the list:
Original data:10; I use it as:11
Original data:20; I use it as:21
Original data:30; I use it as:31
Original data:200; I use it as:210
Original data:150; I use it as:160
Original data:70; I use it as:80
InvestigateNumberVisitor is about to visit the list:
Is small-1 greater than 10 ? No
Is small-2 greater than 10 ? Yes
Is small-3 greater than 10 ? Yes
Is big-1 greater than 100 ? Yes
Is big-2 greater than 100 ? Yes
Is big-3 greater than 100 ? No

You can download the full code for this modified example from the Apress website. I merged this in the namespace called VisitorPatternDemo2.

13.8 I see that you are initializing numberList with objects for SmallNumber and BigNumber. Is it mandatory to create such a structure?

No. I make a container that helps the client to visit smoothly in one shot. In a different variation, you could see that you initialize an empty list first and add (or remove) elements to this inside client code before you traverse the list.

To understand the previous line, you can refer to demonstration 2, where I made the container class inside the client code only.

Using Visitor Pattern and Composite Pattern Together

In demonstration 1, you saw an example of the Visitor design pattern, and in the Q&A session, you went through an extended version of it. Now I’ll show you another implementation, but this time, I combine it with the Composite pattern.

Let’s consider the example of the Composite design pattern from Chapter 11. In that example, there is a college with two different departments. Each of these departments has one head of department (HOD) and multiple professors/lecturers. All HODs report to the principal of the college.

Figure 13-3 shows the tree structure for this example. The college structure is the same as described in Chapter 11. The mathematics lecturers/teachers are M. Joy and M. Roony, and the CSE teachers are C. Sam, C. Jones, and C. Marium. These lecturers do not supervise anyone, so they are treated as leaf nodes in the tree diagram. Dr. S. Som is the principal and holds the highest position. Two HODs (Mrs. S. Das (HOD-Math) and Mr. V. Sarcar (HOD-Comp.Sc) reports to the principal. The HODs and principal are non-leaf nodes.
../images/463942_2_En_13_Chapter/463942_2_En_13_Fig3_HTML.jpg
Figure 13-3

Tree structure of the Composite design example

Now suppose that the principal of the college wants to promote some of the employees. Let’s say that teaching experience is the only criteria for promotion, but the criteria varies between senior teachers (branch nodes) and junior teachers (leaf nodes) as follows: for a junior teacher, the minimum criteria for promotion is 12 years, and for senior teachers, it is 15 years.

If you understand demonstration 1, you realize that the promotion criteria may change in the future, and there may be additional requirements from higher authorities. So, the Visitor pattern is a perfect fit to fulfill the current requirements. This is why in the upcoming example, you see that a new property and a new method are added to the Employee interface; it should be easy to understand with the supportive comments.
// Newly added for this example
// To set years of Experience
double Experience { get; set; }
// Newly added for this example
void Accept(IVisitor visitor);
Following the design in the demonstration 1, let’s make a visitor interface called IVisitor with method called VisitEmployee(...), which has two overloaded versions. Here is the visitor hierarchy.
    /// <summary>
    /// Visitor interface
    /// </summary>
    interface IVisitor
    {
        // To visit leaf nodes
        void VisitEmployees(Employee employee);
        // To visit composite nodes
        void VisitEmployees(CompositeEmployee employee);
    }
    /// <summary>
    /// Concrete visitor class-PromotionCheckerVisitor
    /// </summary>
    class PromotionCheckerVisitor : IVisitor
    {
        string eligibleForPromotion = String.Empty;
        public void VisitEmployees(CompositeEmployee employee)
        {
            //We'll promote them if experience is greater than 15 years
            eligibleForPromotion = employee.Experience > 15 ? "Yes" : "No";
            Console.WriteLine($" { employee.Name } from {employee.Dept} is eligible for promotion? :{eligibleForPromotion}");
        }
        public void VisitEmployees(Employee employee)
        {
            //We'll promote them if experience is greater than 12 years
            eligibleForPromotion = employee.Experience > 12 ? "Yes" : "No";
            Console.WriteLine($" { employee.Name } from {employee.Dept} is eligible for promotion? :{eligibleForPromotion}");
        }
}
This time, I make the container (a List data structure, called participants) in client code. When a visitor gathers the necessary details from this college structure, it can show the eligible candidates for promotion, which is the reason to include the following code segment.
Console.WriteLine(" ***Visitor starts visiting our composite structure*** ");
IVisitor visitor = new PromotionCheckerVisitor();
//Visitor is traversing the participant list
foreach ( IEmployee  emp in participants)
   {
      emp.Accept(visitor);
   }

The visitor is collecting the data one piece at a time from the original college structure without making any modifications to it. Once the collection process is over, the visitor analyzes the data to display the intended results. To understand this visually, you can follow the arrows in Figures 13-4 through 13-8. The principal is at the top of the organization, so you can assume that he receives no promotion.

Step 1

Figure 13-4 shows step 1.
../images/463942_2_En_13_Chapter/463942_2_En_13_Fig4_HTML.jpg
Figure 13-4

Step 1

Step 2

Figure 13-5 shows step 2.
../images/463942_2_En_13_Chapter/463942_2_En_13_Fig5_HTML.jpg
Figure 13-5

Step 2

Step 3

Figure 13-6 shows step 3.
../images/463942_2_En_13_Chapter/463942_2_En_13_Fig6_HTML.jpg
Figure 13-6

Step 3

Step 4

Figure 13-7 shows step 4.
../images/463942_2_En_13_Chapter/463942_2_En_13_Fig7_HTML.jpg
Figure 13-7

Step 4

Step 5

Figure 13-8 shows step 5.
../images/463942_2_En_13_Chapter/463942_2_En_13_Fig8_HTML.jpg
Figure 13-8

Step 5

And so on...

I followed a similar design in demonstration 1, and the code example is built on top of the only demonstration in Chapter 11. For brevity, I do not include the class diagram and Solution Explorer view for this example. So, go directly through the following implementation.

Demonstration 2

Here’s the implementation.
using System;
using System.Collections.Generic;
namespace VisitorWithCompositePattern
{
    interface IEmployee
    {
        //To set an employee name
        string Name { get; set; }
        //To set an employee department
        string Dept { get; set; }
        //To set an employee designation
        string Designation { get; set; }
        //To display an employee details
        void DisplayDetails();
        //Newly added for this example
        //To set years of Experience
        double Experience { get; set; }
        //Newly added for this example
        void Accept(IVisitor visitor);
    }
    //Leaf node
    class Employee : IEmployee
    {
        public string Name { get; set; }
        public string Dept { get; set; }
        public string Designation { get; set; }
        public double Experience { get; set; }
        //Details of a leaf node
        public void DisplayDetails()
        {
            Console.WriteLine($"{Name} works in { Dept} department.Designation:{Designation}.Experience : {Experience} years.");
        }
        public void Accept(IVisitor visitor)
        {
            visitor.VisitEmployees(this);
        }
    }
    //Non-leaf node
    class CompositeEmployee : IEmployee
    {
        public string Name { get; set; }
        public string Dept { get; set; }
        public string Designation { get; set; }
        public double Experience { get; set; }
        //The container for child objects
        //private List<IEmployee> subordinateList = new List<IEmployee>();
        //Making it public now
        public List<IEmployee> subordinateList = new List<IEmployee>();
        //To add an employee
        public void AddEmployee(IEmployee e)
        {
            subordinateList.Add(e);
        }
        //To remove an employee
        public void RemoveEmployee(IEmployee e)
        {
            subordinateList.Remove(e);
        }
        //Details of a composite node
        public void DisplayDetails()
        {
            Console.WriteLine($" {Name} works in {Dept} department.Designation:{Designation}.Experience : {Experience} years.");
            foreach (IEmployee e in subordinateList)
            {
                e.DisplayDetails();
            }
        }
        public void Accept(IVisitor visitor)
        {
            visitor.VisitEmployees(this);
        }
    }
    /// <summary>
    /// Visitor interface
    /// </summary>
    interface IVisitor
    {
        //To visit leaf nodes
        void VisitEmployees(Employee employee);
        //To visit composite nodes
        void VisitEmployees(CompositeEmployee employee);
    }
    /// <summary>
    /// Concrete visitor class-PromotionCheckerVisitor
    /// </summary>
    class PromotionCheckerVisitor : IVisitor
    {
        string eligibleForPromotion = String.Empty;
        public void VisitEmployees(CompositeEmployee employee)
        {
           /*
            We'll promote them if experience is greater than 15 years.
            */
            eligibleForPromotion = employee.Experience > 15 ? "Yes" : "No";
            Console.WriteLine($"{ employee.Name } from {employee.Dept} is eligible for promotion? :{eligibleForPromotion}");
        }
        public void VisitEmployees(Employee employee)
        {
           /*
            We'll promote them if experience is greater
            than 12 years.
            */
            eligibleForPromotion = employee.Experience > 12 ? "Yes" : "No";
            Console.WriteLine($"{ employee.Name } from {employee.Dept} is eligible for promotion? :{eligibleForPromotion}");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Visitor Pattern with Composite Pattern Demo. ***");
            #region Mathematics department
            //2 lecturers work in Mathematics department
            Employee mathTeacher1 = new Employee { Name = "M.Joy", Dept = "Mathematic", Designation = "Lecturer" ,Experience=13.7};
            Employee mathTeacher2 = new Employee { Name = "M.Roony", Dept = "Mathematics", Designation = "Lecturer", Experience = 6.5 };
      //The college has a Head of Department in Mathematics
            CompositeEmployee hodMaths = new CompositeEmployee { Name = "Mrs.S.Das", Dept = "Maths", Designation = "HOD-Maths", Experience = 14 };
      //Lecturers of Mathematics directly reports to HOD-Maths
            hodMaths.AddEmployee(mathTeacher1);
            hodMaths.AddEmployee(mathTeacher2);
            #endregion
            #region Computer Science department
            //3 lecturers work in Computer Sc. department
            Employee cseTeacher1 = new Employee { Name = "C.Sam", Dept = "Computer Science", Designation = "Lecturer", Experience = 10.2 };
            Employee cseTeacher2 = new Employee { Name = "C.Jones", Dept = "Computer Science.", Designation = "Lecturer", Experience = 13.5 };
            Employee cseTeacher3 = new Employee { Name = "C.Marium", Dept = "Computer Science", Designation = "Lecturer", Experience = 7.3 };
    //The college has a Head of Department in Computer science
            CompositeEmployee hodCompSc = new CompositeEmployee { Name = "Mr. V.Sarcar", Dept = "Computer Sc.", Designation = "HOD-Computer Sc.", Experience = 16.5 };
    //Lecturers of Computer Sc. directly reports to HOD-CSE
            hodCompSc.AddEmployee(cseTeacher1);
            hodCompSc.AddEmployee(cseTeacher2);
            hodCompSc.AddEmployee(cseTeacher3);
            #endregion
            #region Top level management
            //The college also has a Principal
            CompositeEmployee principal = new CompositeEmployee { Name = "Dr.S.Som", Dept = "Planning-Supervising-Managing", Designation = "Principal", Experience = 21 };
           /*
            Head of Departments's of Maths and Computer Science directly reports to Principal.
            */
            principal.AddEmployee(hodMaths);
            principal.AddEmployee(hodCompSc);
            #endregion
           /*
           Printing the leaf-nodes and branches in the same way i.e. in each case, we are calling DisplayDetails() method.
           */
            Console.WriteLine(" Details of a college structure is as follows:");
            //Prints the complete structure
            principal.DisplayDetails();
            List<IEmployee> participants = new List<IEmployee>();
            //For employees who directly reports to Principal
            foreach (IEmployee e in principal.subordinateList)
            {
                participants.Add(e);
            }
            //For employees who directly reports to HOD-Maths
            foreach (IEmployee e in hodMaths.subordinateList)
            {
                participants.Add(e);
            }
           //For employees who directly reports to HOD-Comp.Sc
            foreach (IEmployee e in hodCompSc.subordinateList)
            {
                participants.Add(e);
            }
            Console.WriteLine(" ***Visitor starts visiting our composite structure*** ");
            IVisitor visitor = new PromotionCheckerVisitor();
           /*
           Principal is already holding the highest position.
           We are not checking whether he is eligible
           for promotion or not.
           */
            //principal.Accept(visitor);
            //Visitor is traversing the participant list
            foreach ( IEmployee  emp in participants)
            {
                emp.Accept(visitor);
            }
            //Wait for user
            Console.ReadKey();
        }
    }
}

Output

Here’s the output. Some portions are in bold to show you that the visitor was able to complete its job successfully.
***Visitor Pattern with Composite Pattern Demo. ***
Details of a college structure is as follows:
Dr.S.Som works in Planning-Supervising-Managing department.Designation:Principal.Experience : 21 years.
Mrs.S.Das works in Maths department.Designation:HOD-Maths.Experience : 14 years.
M.Joy works in Mathematic department.Designation:Lecturer.Experience : 13.7 years.
M.Roony works in Mathematics department.Designation:Lecturer.Experience : 6.5 years.
Mr. V.Sarcar works in Computer Sc. department.Designation:HOD-Computer Sc..Experience : 16.5 years.
C.Sam works in Computer Science department.Designation:Lecturer.Experience : 10.2 years.
C.Jones works in Computer Science. department.Designation:Lecturer.Experience : 13.5 years.
C.Marium works in Computer Science department.Designation:Lecturer.Experience : 7.3 years.
***Visitor starts visiting our composite structure***
Mrs.S.Das from Maths is eligible for promotion? :No
Mr. V.Sarcar from Computer Sc. is eligible for promotion? :Yes
M.Joy from Mathematic is eligible for promotion? :Yes
M.Roony from Mathematics is eligible for promotion? :No
C.Sam from Computer Science is eligible for promotion? :No
C.Jones from Computer Science. is eligible for promotion? :Yes
C.Marium from Computer Science is eligible for promotion? :No
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.172.146