Visitor Pattern In C#

In this article I will explain the visitor pattern in detail, but before jumping to Visitor Pattern, let’s have a look at the following screenshot for Design Patterns and its types.

Design Pattern

There are a lot of design patterns (around 35-40), I am not going to explain other design pattern here otherwise the topic will become too large.

Let’s go to Visitor Pattern

Visitor is one of the most important design pattern of C#. As you can see in the above  screenshot, it falls under the category of Behavioural design pattern that means it will work and change the behaviour of the class.

Visitor pattern is used for separation of Business logic and algorithm from an object data structure i.e. it is used for separating logic from actual data structure.

Due to the separation of data structure and its logic we can add new logic without changing the current data structure and vice-versa.

Visitor Pattern 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 (as per GOF).

Visitor Pattern

To co-relate the example with real world let’s take an example of Employee Monthly Salary, investment and Tax calculation.

Employee Monthly Salary

Let’s calculate Income Tax, Annual Investment, Annual Net Earning… with C#.

Without Visitor Pattern

Without Visitor Pattern

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Globalization;  
  4. namespace Earning_TaxInIndiaWithoutVisitorPattern  
  5. {  
  6.     class Program  
  7.     {  
  8.         static void Main(string[] args)  
  9.         {  
  10.             Employee emp = new Employee  
  11.             {  
  12.                 EmployeeId = "XYZ1001", EmployeeName = "Banketeshvar Narayan"  
  13.             };  
  14.             AddDataForEmployee(emp);#  
  15.             region Calculate Net Earning of the Year  
  16.             double NetEarningoftheYear = 0.0;  
  17.             foreach(var monthlySalary_Earning in emp.MonthlySalary_Earnings)  
  18.             {  
  19.                 NetEarningoftheYear += (monthlySalary_Earning.BasicSalary + monthlySalary_Earning.ConveyanceAllowance + monthlySalary_Earning.FoodCard_Bill + monthlySalary_Earning.HRAExemption + monthlySalary_Earning.MedicalAllowance + monthlySalary_Earning.OtherBills + monthlySalary_Earning.PersonalAllowance + monthlySalary_Earning.TelephoneBill);  
  20.             }  
  21.             foreach(var monthlySalary_Deduction in emp.MonthlySalary_Deductions)  
  22.             {  
  23.                 NetEarningoftheYear -= (monthlySalary_Deduction.ProvidentFund_EmployeeContribution + monthlySalary_Deduction.ProvidentFund_EmployerContribution + monthlySalary_Deduction.ProfessionTax + monthlySalary_Deduction.OtherDeduction);  
  24.             }#  
  25.             endregion# region Calculate TaxableAmount  
  26.             double TaxableAmount = 0.0;  
  27.             foreach(var monthlySalary_Earning in emp.MonthlySalary_Earnings)  
  28.             {  
  29.                 TaxableAmount += (monthlySalary_Earning.BasicSalary + monthlySalary_Earning.HRAExemption + monthlySalary_Earning.MedicalAllowance + monthlySalary_Earning.PersonalAllowance);  
  30.                 //Non Taxable parts  
  31.                 //monthlySalary_Earning.FoodCard_Bill   
  32.                 //monthlySalary_Earning.ConveyanceAllowance  
  33.                 //monthlySalary_Earning.TelephoneBill  
  34.                 //monthlySalary_Earning.OtherBills  
  35.             }  
  36.             foreach(var monthlySalary_Deduction in emp.MonthlySalary_Deductions)  
  37.             {  
  38.                 TaxableAmount -= (monthlySalary_Deduction.ProvidentFund_EmployeeContribution + monthlySalary_Deduction.ProvidentFund_EmployerContribution + monthlySalary_Deduction.ProfessionTax + monthlySalary_Deduction.OtherDeduction);  
  39.             }  
  40.             foreach(var monthlyExpense in emp.MonthlyExpenses)  
  41.             {  
  42.                 TaxableAmount -= monthlyExpense.MonthlyRent;  
  43.             }  
  44.             foreach(var annualInvestment in emp.AnnualInvestments)  
  45.             {  
  46.                 TaxableAmount -= annualInvestment.InvestmentAmmount;  
  47.             }#  
  48.             endregion  
  49.             Console.WriteLine("Annual Net Earning Amount : {0}", NetEarningoftheYear);  
  50.             Console.WriteLine("Annual Taxable Amount : {0}", TaxableAmount);  
  51.         }  
  52.         private static void AddDataForEmployee(Employee emp)  
  53.         {  
  54.             for (int i = 1; i <= 12; i++)  
  55.             {  
  56.                 emp.MonthlySalary_Earnings.Add(new MonthlySalary_Earning  
  57.                 {  
  58.                     MonthName = DateTimeFormatInfo.CurrentInfo.GetMonthName(i),  
  59.                         BasicSalary = 120000,  
  60.                         HRAExemption = 50000,  
  61.                         ConveyanceAllowance = 1600,  
  62.                         PersonalAllowance = 45000,  
  63.                         MedicalAllowance = 1500,  
  64.                         TelephoneBill = 2500,  
  65.                         FoodCard_Bill = 3000,  
  66.                         OtherBills = 35000  
  67.                 });  
  68.                 emp.MonthlySalary_Deductions.Add(new MonthlySalary_Deduction  
  69.                 {  
  70.                     MonthName = DateTimeFormatInfo.CurrentInfo.GetMonthName(i),  
  71.                         ProvidentFund_EmployeeContribution = 8000,  
  72.                         ProvidentFund_EmployerContribution = 8000,  
  73.                         OtherDeduction = 700,  
  74.                         ProfessionTax = 200,  
  75.                         TDS = 15000  
  76.                 });  
  77.                 emp.MonthlyExpenses.Add(new MonthlyExpense  
  78.                 {  
  79.                     MonthName = DateTimeFormatInfo.CurrentInfo.GetMonthName(1), MonthlyRent = 10000  
  80.                 });  
  81.             }  
  82.             emp.AnnualInvestments.Add(new AnnualInvestment  
  83.             {  
  84.                 InvestmentDetails = "MediclaimPolicy", InvestmentAmmount = 15000  
  85.             });  
  86.             emp.AnnualInvestments.Add(new AnnualInvestment  
  87.             {  
  88.                 InvestmentDetails = "MediclaimPolicyforParents", InvestmentAmmount = 25000  
  89.             });  
  90.             emp.AnnualInvestments.Add(new AnnualInvestment  
  91.             {  
  92.                 InvestmentDetails = "HouseLoan", InvestmentAmmount = 0.0  
  93.             });  
  94.             emp.AnnualInvestments.Add(new AnnualInvestment  
  95.             {  
  96.                 InvestmentDetails = "EducationLoan", InvestmentAmmount = 0.0  
  97.             });  
  98.             emp.AnnualInvestments.Add(new AnnualInvestment  
  99.             {  
  100.                 InvestmentDetails = "OtherInvestment", InvestmentAmmount = 5000  
  101.             });  
  102.             emp.AnnualInvestments.Add(new AnnualInvestment  
  103.             {  
  104.                 InvestmentDetails = "RGESS", InvestmentAmmount = 5500  
  105.             });  
  106.             emp.AnnualInvestments.Add(new AnnualInvestment  
  107.             {  
  108.                 InvestmentDetails = "Section80Cn80CCD_ExceptPF", InvestmentAmmount = 100000  
  109.             });  
  110.             //emp.AnnualInvestments.Add(new AnnualInvestment  
  111.             //{  
  112.             // //MediclaimPolicy = 15000,  
  113.             // //MediclaimPolicyforParents = 25000,  
  114.             // //HouseLoan = 0.0,  
  115.             // //EducationLoan = 0.0,  
  116.             // //OtherInvestment = 5000,  
  117.             // //RGESS = 5500,  
  118.             // //Section80Cn80CCD_ExceptPF = 100000  
  119.             //});  
  120.         }  
  121.     }#  
  122.     region Employee  
  123.     public class Employee  
  124.     {  
  125.         public string EmployeeId  
  126.         {  
  127.             get;  
  128.             set;  
  129.         }  
  130.         public string EmployeeName  
  131.         {  
  132.             get;  
  133.             set;  
  134.         }  
  135.         public List < MonthlySalary_Earning > MonthlySalary_Earnings = new List < MonthlySalary_Earning > ();  
  136.         public List < MonthlySalary_Deduction > MonthlySalary_Deductions = new List < MonthlySalary_Deduction > ();  
  137.         public List < AnnualInvestment > AnnualInvestments = new List < AnnualInvestment > ();  
  138.         public List < MonthlyExpense > MonthlyExpenses = new List < MonthlyExpense > ();  
  139.     }#  
  140.     endregion# region MonthlySalary_Earning  
  141.     public class MonthlySalary_Earning  
  142.     {  
  143.         public string MonthName  
  144.         {  
  145.             get;  
  146.             set;  
  147.         }  
  148.         public double BasicSalary  
  149.         {  
  150.             get;  
  151.             set;  
  152.         }  
  153.         public double HRAExemption  
  154.         {  
  155.             get;  
  156.             set;  
  157.         }  
  158.         public double ConveyanceAllowance  
  159.         {  
  160.             get;  
  161.             set;  
  162.         }  
  163.         public double PersonalAllowance  
  164.         {  
  165.             get;  
  166.             set;  
  167.         }  
  168.         public double MedicalAllowance  
  169.         {  
  170.             get;  
  171.             set;  
  172.         }  
  173.         public double TelephoneBill  
  174.         {  
  175.             get;  
  176.             set;  
  177.         }  
  178.         public double FoodCard_Bill  
  179.         {  
  180.             get;  
  181.             set;  
  182.         }  
  183.         public double OtherBills  
  184.         {  
  185.             get;  
  186.             set;  
  187.         }  
  188.     }#  
  189.     endregion# region MonthlySalary_Deduction  
  190.     public class MonthlySalary_Deduction  
  191.     {  
  192.         public string MonthName  
  193.         {  
  194.             get;  
  195.             set;  
  196.         }  
  197.         public double ProvidentFund_EmployeeContribution  
  198.         {  
  199.             get;  
  200.             set;  
  201.         }  
  202.         public double ProvidentFund_EmployerContribution  
  203.         {  
  204.             get;  
  205.             set;  
  206.         }  
  207.         public double ProfessionTax  
  208.         {  
  209.             get;  
  210.             set;  
  211.         }  
  212.         public double TDS  
  213.         {  
  214.             get;  
  215.             set;  
  216.         }  
  217.         public double OtherDeduction  
  218.         {  
  219.             get;  
  220.             set;  
  221.         }  
  222.     }#  
  223.     endregion# region AnnualInvestment  
  224.     public class AnnualInvestment  
  225.     {  
  226.         public string InvestmentDetails  
  227.         {  
  228.             get;  
  229.             set;  
  230.         }  
  231.         public double InvestmentAmmount  
  232.         {  
  233.             get;  
  234.             set;  
  235.         }  
  236.         //public double MediclaimPolicy { get; set; }  
  237.         //public double MediclaimPolicyforParents { get; set; }  
  238.         //public double HouseLoan { get; set; }  
  239.         //public double EducationLoan { get; set; }  
  240.         //public double Section80Cn80CCD_ExceptPF { get; set; }  
  241.         //public double RGESS { get; set; }  
  242.         //public double OtherInvestment { get; set; }   
  243.     }#  
  244.     endregion# region MonthlyExpense  
  245.     public class MonthlyExpense  
  246.     {  
  247.         public string MonthName  
  248.         {  
  249.             get;  
  250.             set;  
  251.         }  
  252.         public double MonthlyRent  
  253.         {  
  254.             get;  
  255.             set;  
  256.         }  
  257.     }#  
  258.     endregion  
  259. }  
With Visitor Pattern

With Visitor Pattern
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Globalization;  
  4. namespace Earning_TaxationInIndiaWithVisitorPattern  
  5. {  
  6.     class Program  
  7.     {  
  8.         static void Main(string[] args)  
  9.         {  
  10.             Employee emp = new Employee  
  11.             {  
  12.                 EmployeeId = "XYZ1001", EmployeeName = "Banketeshvar Narayan Sharma"  
  13.             };  
  14.             AddDataForEmployee(emp);  
  15.             var netAnnualEarningVisitor = new NetAnnualEarningVisitor();  
  16.             var annualTaxableAmount = new TaxableAmountVisitor();  
  17.             emp.Accept(netAnnualEarningVisitor);  
  18.             emp.Accept(annualTaxableAmount);  
  19.             Console.WriteLine("Annual Net Earning Amount : {0}", netAnnualEarningVisitor.NetEarningoftheYear);  
  20.             Console.WriteLine("Annual Taxable Amount : {0}", annualTaxableAmount.TaxableAmount);  
  21.             Console.ReadKey();  
  22.         }  
  23.         private static void AddDataForEmployee(Employee emp)  
  24.         {  
  25.             for (int i = 1; i <= 12; i++)  
  26.             {  
  27.                 emp.Salaries.Add(new MonthlySalary_Earning  
  28.                 {  
  29.                     MonthName = DateTimeFormatInfo.CurrentInfo.GetMonthName(i),  
  30.                         BasicSalary = 120000,  
  31.                         HRAExemption = 50000,  
  32.                         ConveyanceAllowance = 1600,  
  33.                         PersonalAllowance = 45000,  
  34.                         MedicalAllowance = 1500,  
  35.                         TelephoneBill = 2500,  
  36.                         FoodCard_Bill = 3000,  
  37.                         OtherBills = 35000  
  38.                 });  
  39.                 emp.Salaries.Add(new MonthlySalary_Deduction  
  40.                 {  
  41.                     MonthName = DateTimeFormatInfo.CurrentInfo.GetMonthName(i),  
  42.                         ProvidentFund_EmployeeContribution = 8000,  
  43.                         ProvidentFund_EmployerContribution = 8000,  
  44.                         OtherDeduction = 700,  
  45.                         ProfessionTax = 200,  
  46.                         TDS = 15000  
  47.                 });  
  48.                 emp.Salaries.Add(new MonthlyExpense  
  49.                 {  
  50.                     MonthName = DateTimeFormatInfo.CurrentInfo.GetMonthName(1), MonthlyRent = 10000  
  51.                 });  
  52.             }  
  53.             emp.Salaries.Add(new AnnualInvestment  
  54.             {  
  55.                 InvestmentDetails = "MediclaimPolicy", InvestmentAmmount = 15000  
  56.             });  
  57.             emp.Salaries.Add(new AnnualInvestment  
  58.             {  
  59.                 InvestmentDetails = "MediclaimPolicyforParents", InvestmentAmmount = 25000  
  60.             });  
  61.             emp.Salaries.Add(new AnnualInvestment  
  62.             {  
  63.                 InvestmentDetails = "HouseLoan", InvestmentAmmount = 0.0  
  64.             });  
  65.             emp.Salaries.Add(new AnnualInvestment  
  66.             {  
  67.                 InvestmentDetails = "EducationLoan", InvestmentAmmount = 0.0  
  68.             });  
  69.             emp.Salaries.Add(new AnnualInvestment  
  70.             {  
  71.                 InvestmentDetails = "OtherInvestment", InvestmentAmmount = 5000  
  72.             });  
  73.             emp.Salaries.Add(new AnnualInvestment  
  74.             {  
  75.                 InvestmentDetails = "RGESS", InvestmentAmmount = 5500  
  76.             });  
  77.             emp.Salaries.Add(new AnnualInvestment  
  78.             {  
  79.                 InvestmentDetails = "Section80Cn80CCD_ExceptPF", InvestmentAmmount = 100000  
  80.             });  
  81.         }  
  82.     }  
  83.     public class NetAnnualEarningVisitor: IVisitor  
  84.     {  
  85.         public double NetEarningoftheYear  
  86.         {  
  87.             get;  
  88.             set;  
  89.         }  
  90.         public void Visit(MonthlySalary_Earning monthlySalary_Earning)  
  91.         {  
  92.             NetEarningoftheYear += (monthlySalary_Earning.BasicSalary + monthlySalary_Earning.ConveyanceAllowance + monthlySalary_Earning.FoodCard_Bill + monthlySalary_Earning.HRAExemption + monthlySalary_Earning.MedicalAllowance + monthlySalary_Earning.OtherBills + monthlySalary_Earning.PersonalAllowance + monthlySalary_Earning.TelephoneBill);  
  93.         }  
  94.         public void Visit(MonthlySalary_Deduction monthlySalary_Deduction)  
  95.         {  
  96.             NetEarningoftheYear -= (monthlySalary_Deduction.ProvidentFund_EmployeeContribution + monthlySalary_Deduction.ProvidentFund_EmployerContribution + monthlySalary_Deduction.ProfessionTax + monthlySalary_Deduction.OtherDeduction);  
  97.         }  
  98.         public void Visit(AnnualInvestment annualInvestment)  
  99.         {  
  100.             // do nothing  
  101.         }  
  102.         public void Visit(MonthlyExpense monthlyExpense)  
  103.         {  
  104.             //do nothing  
  105.         }  
  106.     }  
  107.     public class TaxableAmountVisitor: IVisitor  
  108.     {  
  109.         public double TaxableAmount  
  110.         {  
  111.             get;  
  112.             set;  
  113.         }  
  114.         public void Visit(MonthlySalary_Earning monthlySalary_Earning)  
  115.         {  
  116.             TaxableAmount += (monthlySalary_Earning.BasicSalary + monthlySalary_Earning.HRAExemption + monthlySalary_Earning.MedicalAllowance + monthlySalary_Earning.PersonalAllowance);  
  117.         }  
  118.         public void Visit(MonthlySalary_Deduction monthlySalary_Deduction)  
  119.         {  
  120.             TaxableAmount -= (monthlySalary_Deduction.ProvidentFund_EmployeeContribution + monthlySalary_Deduction.ProvidentFund_EmployerContribution + monthlySalary_Deduction.ProfessionTax + monthlySalary_Deduction.OtherDeduction);  
  121.         }  
  122.         public void Visit(MonthlyExpense monthlyExpense)  
  123.         {  
  124.             TaxableAmount -= monthlyExpense.MonthlyRent;  
  125.         }  
  126.         public void Visit(AnnualInvestment annualInvestment)  
  127.         {  
  128.             TaxableAmount -= annualInvestment.InvestmentAmmount;  
  129.         }  
  130.     }  
  131.     public interface ISalary  
  132.     {  
  133.         void Accept(IVisitor visitor);  
  134.     }  
  135.     public interface IVisitor  
  136.     {  
  137.         void Visit(MonthlySalary_Earning monthlySalary_Earning);  
  138.         void Visit(MonthlySalary_Deduction monthlySalary_Deduction);  
  139.         void Visit(MonthlyExpense monthlyExpense);  
  140.         void Visit(AnnualInvestment annualInvestment);  
  141.     }#  
  142.     region Employee  
  143.     public class Employee: ISalary  
  144.     {  
  145.         public string EmployeeId  
  146.         {  
  147.             get;  
  148.             set;  
  149.         }  
  150.         public string EmployeeName  
  151.         {  
  152.             get;  
  153.             set;  
  154.         }  
  155.         public List < ISalary > Salaries = new List < ISalary > ();  
  156.         public void Accept(IVisitor visitor)  
  157.         {  
  158.             foreach(var salary in Salaries)  
  159.             {  
  160.                 salary.Accept(visitor);  
  161.             }  
  162.         }  
  163.     }#  
  164.     endregion# region MonthlySalary_Earning  
  165.     public class MonthlySalary_Earning: ISalary  
  166.     {  
  167.         public string MonthName  
  168.         {  
  169.             get;  
  170.             set;  
  171.         }  
  172.         public double BasicSalary  
  173.         {  
  174.             get;  
  175.             set;  
  176.         }  
  177.         public double HRAExemption  
  178.         {  
  179.             get;  
  180.             set;  
  181.         }  
  182.         public double ConveyanceAllowance  
  183.         {  
  184.             get;  
  185.             set;  
  186.         }  
  187.         public double PersonalAllowance  
  188.         {  
  189.             get;  
  190.             set;  
  191.         }  
  192.         public double MedicalAllowance  
  193.         {  
  194.             get;  
  195.             set;  
  196.         }  
  197.         public double TelephoneBill  
  198.         {  
  199.             get;  
  200.             set;  
  201.         }  
  202.         public double FoodCard_Bill  
  203.         {  
  204.             get;  
  205.             set;  
  206.         }  
  207.         public double OtherBills  
  208.         {  
  209.             get;  
  210.             set;  
  211.         }  
  212.         public void Accept(IVisitor visitor)  
  213.         {  
  214.             visitor.Visit(this);  
  215.         }  
  216.     }#  
  217.     endregion# region MonthlySalary_Deduction  
  218.     public class MonthlySalary_Deduction: ISalary  
  219.     {  
  220.         public string MonthName  
  221.         {  
  222.             get;  
  223.             set;  
  224.         }  
  225.         public double ProvidentFund_EmployeeContribution  
  226.         {  
  227.             get;  
  228.             set;  
  229.         }  
  230.         public double ProvidentFund_EmployerContribution  
  231.         {  
  232.             get;  
  233.             set;  
  234.         }  
  235.         public double ProfessionTax  
  236.         {  
  237.             get;  
  238.             set;  
  239.         }  
  240.         public double TDS  
  241.         {  
  242.             get;  
  243.             set;  
  244.         }  
  245.         public double OtherDeduction  
  246.         {  
  247.             get;  
  248.             set;  
  249.         }  
  250.         public void Accept(IVisitor visitor)  
  251.         {  
  252.             visitor.Visit(this);  
  253.         }  
  254.     }#  
  255.     endregion# region AnnualInvestment  
  256.     public class AnnualInvestment: ISalary  
  257.     {  
  258.         public string InvestmentDetails  
  259.         {  
  260.             get;  
  261.             set;  
  262.         }  
  263.         public double InvestmentAmmount  
  264.         {  
  265.             get;  
  266.             set;  
  267.         }  
  268.         public void Accept(IVisitor visitor)  
  269.         {  
  270.             visitor.Visit(this);  
  271.         }  
  272.     }#  
  273.     endregion# region MonthlyExpense  
  274.     public class MonthlyExpense: ISalary  
  275.     {  
  276.         public string MonthName  
  277.         {  
  278.             get;  
  279.             set;  
  280.         }  
  281.         public double MonthlyRent  
  282.         {  
  283.             get;  
  284.             set;  
  285.         }  
  286.         public void Accept(IVisitor visitor)  
  287.         {  
  288.             visitor.Visit(this);  
  289.         }  
  290.     }#  
  291.     endregion  
  292. }