Introduction
In this article, I will explain the visitor pattern in detail, but before jumping to the Visitor Pattern, let’s have a look at the following screenshot for Design Patterns and their types.
There are a lot of design patterns (around 35-40). I am not going to explain other design patterns here otherwise, the topic will become too large.
What is the Visitor Pattern?
Visitor is one of the most important design patterns of C#. As you can see in the above screenshot, it falls under the category of Behavioural design pattern, which means it will work and change the behavior of the class.
Visitor pattern is used for the separation of Business logic and algorithm from an object data structure i.e. it is used for separating logic from the 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 represents 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).
To co-relate the example to the real world, let’s take the example of Employee Monthly Salary, investment, and Tax calculation.
Let's calculate Income Tax, Annual Investment, and Annual Net Earning&hellip, with C#.
Implementing the Without Visitor Pattern in C#
using System;
using System.Collections.Generic;
using System.Globalization;
namespace Earning_TaxInIndiaWithoutVisitorPattern
{
class Program
{
static void Main(string[] args)
{
Employee emp = new Employee
{
EmployeeId = "XYZ1001",
EmployeeName = "Banketeshvar Narayan"
};
AddDataForEmployee(emp);
// Calculate Net Earning of the Year
double NetEarningoftheYear = 0.0;
foreach (var monthlySalary_Earning in emp.MonthlySalary_Earnings)
{
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);
}
foreach (var monthlySalary_Deduction in emp.MonthlySalary_Deductions)
{
NetEarningoftheYear -= (monthlySalary_Deduction.ProvidentFund_EmployeeContribution +
monthlySalary_Deduction.ProvidentFund_EmployerContribution +
monthlySalary_Deduction.ProfessionTax +
monthlySalary_Deduction.OtherDeduction);
}
// Calculate TaxableAmount
double TaxableAmount = 0.0;
foreach (var monthlySalary_Earning in emp.MonthlySalary_Earnings)
{
TaxableAmount += (monthlySalary_Earning.BasicSalary +
monthlySalary_Earning.HRAExemption +
monthlySalary_Earning.MedicalAllowance +
monthlySalary_Earning.PersonalAllowance);
// Non Taxable parts
// monthlySalary_Earning.FoodCard_Bill
// monthlySalary_Earning.ConveyanceAllowance
// monthlySalary_Earning.TelephoneBill
// monthlySalary_Earning.OtherBills
}
foreach (var monthlySalary_Deduction in emp.MonthlySalary_Deductions)
{
TaxableAmount -= (monthlySalary_Deduction.ProvidentFund_EmployeeContribution +
monthlySalary_Deduction.ProvidentFund_EmployerContribution +
monthlySalary_Deduction.ProfessionTax +
monthlySalary_Deduction.OtherDeduction);
}
foreach (var monthlyExpense in emp.MonthlyExpenses)
{
TaxableAmount -= monthlyExpense.MonthlyRent;
}
foreach (var annualInvestment in emp.AnnualInvestments)
{
TaxableAmount -= annualInvestment.InvestmentAmmount;
}
Console.WriteLine("Annual Net Earning Amount: {0}", NetEarningoftheYear);
Console.WriteLine("Annual Taxable Amount: {0}", TaxableAmount);
}
private static void AddDataForEmployee(Employee emp)
{
for (int i = 1; i <= 12; i++)
{
emp.MonthlySalary_Earnings.Add(new MonthlySalary_Earning
{
MonthName = DateTimeFormatInfo.CurrentInfo.GetMonthName(i),
BasicSalary = 120000,
HRAExemption = 50000,
ConveyanceAllowance = 1600,
PersonalAllowance = 45000,
MedicalAllowance = 1500,
TelephoneBill = 2500,
FoodCard_Bill = 3000,
OtherBills = 35000
});
emp.MonthlySalary_Deductions.Add(new MonthlySalary_Deduction
{
MonthName = DateTimeFormatInfo.CurrentInfo.GetMonthName(i),
ProvidentFund_EmployeeContribution = 8000,
ProvidentFund_EmployerContribution = 8000,
OtherDeduction = 700,
ProfessionTax = 200,
TDS = 15000
});
emp.MonthlyExpenses.Add(new MonthlyExpense
{
MonthName = DateTimeFormatInfo.CurrentInfo.GetMonthName(1),
MonthlyRent = 10000
});
}
emp.AnnualInvestments.Add(new AnnualInvestment
{
InvestmentDetails = "MediclaimPolicy",
InvestmentAmmount = 15000
});
emp.AnnualInvestments.Add(new AnnualInvestment
{
InvestmentDetails = "MediclaimPolicyforParents",
InvestmentAmmount = 25000
});
emp.AnnualInvestments.Add(new AnnualInvestment
{
InvestmentDetails = "HouseLoan",
InvestmentAmmount = 0.0
});
emp.AnnualInvestments.Add(new AnnualInvestment
{
InvestmentDetails = "EducationLoan",
InvestmentAmmount = 0.0
});
emp.AnnualInvestments.Add(new AnnualInvestment
{
InvestmentDetails = "OtherInvestment",
InvestmentAmmount = 5000
});
emp.AnnualInvestments.Add(new AnnualInvestment
{
InvestmentDetails = "RGESS",
InvestmentAmmount = 5500
});
emp.AnnualInvestments.Add(new AnnualInvestment
{
InvestmentDetails = "Section80Cn80CCD_ExceptPF",
InvestmentAmmount = 100000
});
}
}
public class Employee
{
public string EmployeeId { get; set; }
public string EmployeeName { get; set; }
public List<MonthlySalary_Earning> MonthlySalary_Earnings = new List<MonthlySalary_Earning>();
public List<MonthlySalary_Deduction> MonthlySalary_Deductions = new List<MonthlySalary_Deduction>();
public List<AnnualInvestment> AnnualInvestments = new List<AnnualInvestment>();
public List<MonthlyExpense> MonthlyExpenses = new List<MonthlyExpense>();
}
public class MonthlySalary_Earning
{
public string MonthName { get; set; }
public double BasicSalary { get; set; }
public double HRAExemption { get; set; }
public double ConveyanceAllowance { get; set; }
public double PersonalAllowance { get; set; }
public double MedicalAllowance { get; set; }
public double TelephoneBill { get; set; }
public double FoodCard_Bill { get; set; }
public double OtherBills { get; set; }
}
public class MonthlySalary_Deduction
{
public string MonthName { get; set; }
public double ProvidentFund_EmployeeContribution { get; set; }
public double ProvidentFund_EmployerContribution { get; set; }
public double ProfessionTax { get; set; }
public double TDS { get; set; }
public double OtherDeduction { get; set; }
}
public class AnnualInvestment
{
public string InvestmentDetails { get; set; }
public double InvestmentAmmount { get; set; }
}
public class MonthlyExpense
{
public string MonthName { get; set; }
public double MonthlyRent { get; set;
Implementing the With Visitor Pattern in C#
using System;
using System.Collections.Generic;
using System.Globalization;
namespace Earning_TaxationInIndiaWithVisitorPattern
{
class Program
{
static void Main(string[] args)
{
Employee emp = new Employee
{
EmployeeId = "XYZ1001",
EmployeeName = "Banketeshvar Narayan Sharma"
};
AddDataForEmployee(emp);
var netAnnualEarningVisitor = new NetAnnualEarningVisitor();
var annualTaxableAmount = new TaxableAmountVisitor();
emp.Accept(netAnnualEarningVisitor);
emp.Accept(annualTaxableAmount);
Console.WriteLine("Annual Net Earning Amount: {0}", netAnnualEarningVisitor.NetEarningoftheYear);
Console.WriteLine("Annual Taxable Amount: {0}", annualTaxableAmount.TaxableAmount);
Console.ReadKey();
}
private static void AddDataForEmployee(Employee emp)
{
for (int i = 1; i <= 12; i++)
{
emp. Salaries.Add(new MonthlySalary_Earning
{
MonthName = DateTimeFormatInfo.CurrentInfo.GetMonthName(i),
BasicSalary = 120000,
HRAExemption = 50000,
ConveyanceAllowance = 1600,
PersonalAllowance = 45000,
MedicalAllowance = 1500,
TelephoneBill = 2500,
FoodCard_Bill = 3000,
OtherBills = 35000
});
emp. Salaries.Add(new MonthlySalary_Deduction
{
MonthName = DateTimeFormatInfo.CurrentInfo.GetMonthName(i),
ProvidentFund_EmployeeContribution = 8000,
ProvidentFund_EmployerContribution = 8000,
OtherDeduction = 700,
ProfessionTax = 200,
TDS = 15000
});
emp. Salaries.Add(new MonthlyExpense
{
MonthName = DateTimeFormatInfo.CurrentInfo.GetMonthName(1),
MonthlyRent = 10000
});
}
emp. Salaries.Add(new annual investment
{
InvestmentDetails = "MediclaimPolicy",
InvestmentAmmount = 15000
});
emp. Salaries.Add(new annual investment
{
InvestmentDetails = "MediclaimPolicyforParents",
InvestmentAmmount = 25000
});
emp. Salaries.Add(new annual investment
{
InvestmentDetails = "HouseLoan",
InvestmentAmmount = 0.0
});
emp. Salaries.Add(new annual investment
{
InvestmentDetails = "EducationLoan",
InvestmentAmmount = 0.0
});
emp.Salaries.Add(new annual investment
{
InvestmentDetails = "OtherInvestment",
InvestmentAmmount = 5000
});
emp.Salaries.Add(new annual investment
{
InvestmentDetails = "RGESS",
InvestmentAmmount = 5500
});
emp. Salaries.Add(new annual investment
{
InvestmentDetails = "Section80Cn80CCD_ExceptPF",
InvestmentAmmount = 100000
});
}
}
public class NetAnnualEarningVisitor: IVisitor
{
public double NetEarningoftheYear { get; set; }
public void Visit(MonthlySalary_Earning monthlySalary_Earning)
{
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);
}
public void Visit(MonthlySalary_Deduction monthlySalary_Deduction)
{
NetEarningoftheYear -= (monthlySalary_Deduction.ProvidentFund_EmployeeContribution + monthlySalary_Deduction.ProvidentFund_EmployerContribution + monthlySalary_Deduction.ProfessionTax + monthlySalary_Deduction.OtherDeduction);
}
public void Visit(AnnualInvestment annualInvestment)
{
// do nothing
}
public void Visit(MonthlyExpense monthlyExpense)
{
// do nothing
}
}
public class TaxableAmountVisitor: IVisitor
{
public double TaxableAmount { get; set; }
public void Visit(MonthlySalary_Earning monthlySalary_Earning)
{
TaxableAmount += (monthlySalary_Earning.BasicSalary + monthlySalary_Earning.HRAExemption + monthlySalary_Earning.MedicalAllowance + monthlySalary_Earning.PersonalAllowance);
}
public void Visit(MonthlySalary_Deduction monthlySalary_Deduction)
{
TaxableAmount -= (monthlySalary_Deduction.ProvidentFund_EmployeeContribution + monthlySalary_Deduction.ProvidentFund_EmployerContribution + monthlySalary_Deduction.ProfessionTax + monthlySalary_Deduction.OtherDeduction);
}
public void Visit(MonthlyExpense monthlyExpense)
{
TaxableAmount -= monthlyExpense.MonthlyRent;
}
public void Visit(AnnualInvestment annualInvestment)
{
TaxableAmount -= annualInvestment.InvestmentAmmount;
}
}
public interface ISalary
{
void Accept(IVisitor visitor);
}
public interface IVisitor
{
void Visit(MonthlySalary_Earning monthlySalary_Earning);
void Visit(MonthlySalary_Deduction monthlySalary_Deduction);
void Visit(MonthlyExpense monthlyExpense);
void Visit(AnnualInvestment annualInvestment);
}
Public class Employee: Isalary
{
public string EmployeeId { get; set; }
public string EmployeeName { get; set; }
public List<ISalary> Salaries = new List<ISalary>();
public void Accept(IVisitor visitor)
{
foreach (var salary in Salaries)
{
salary.Accept(visitor);
}
}
}
public class MonthlySalary_Earning : ISalary
{
public string MonthName { get; set; }
public double BasicSalary { get; set; }
public double HRAExemption { get; set; }
public double ConveyanceAllowance { get; set; }
public double PersonalAllowance { get; set; }
public double MedicalAllowance { get; set; }
public double TelephoneBill { get; set; }
public double FoodCard_Bill { get; set; }
public double OtherBills { get; set; }
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}
public class MonthlySalary_Deduction : ISalary
{
public string MonthName { get; set; }
public double ProvidentFund_EmployeeContribution { get; set; }
public double ProvidentFund_E