# Visitor Pattern with C# - Real World Tax Example

2023, Aug 19

The Visitor pattern is a behavioural design pattern, allowing new operations to be created or executed by the object, without modifying its structure. For this to happen a separate algorithm (the visitor) handles the operation. When it comes to real-world scenarios, this article shows a tax example to exemplify the pattern in C#.

## Understanding the problem

How is the Income Tax calculated in your country? The New Zealand tax rate is calculated in tiers, by applying a percentage over a value range:

Personal income

• 39% from $180,001 • 33% from$70,001 to $180,000 • 30%:$48,001 to $70,000 • 17.5%:$14,001 to $48,000 • 10.5%:$0 to $14,000 So if you earn$80,000, it means that 4 tiers will be applied to your income: 10.5%, 17.5%, 30% and 33%, as below:

Income tax rate Income Tax
Tier1 - Income up to $14,000.00, taxed at 10.5% 14,000.00 1,470.00 Tier2 - Income over$14,000.00 and up to $48,000.00, taxed at 17.5% 34,000.00 5,950.00 Tier3 - Income over$48,000.00 and up to $70,000.00, taxed at 30% 22,000.00 6,600.00 Tier4 - Income over$70,000.00 and up to $180,000.00, taxed at 33% 10,000.00 3,300.00 Tier5 - Remaining income over$180,000.00, taxed at 39% 0.00 0.00
Total 80,000.00 17,320.00

And you will pay $17,320.00 The example above was introduced in my previous article about the Decorator Pattern. ## Solving using the pattern Before the calculation starts, we need an object to hold the state, to get the results of Tax calculation. Let's call it IncomeTax.cs: public class IncomeTax { public decimal Income { get; set; } public decimal Tax { get; set; } } The basic idea of this pattern is that a "host" receives a "visitor", so we need to define a contract for both, the host that accepts the visitor1 and the visitor that does the work. Let's call both ITaxCalculator.cs and ITaxVisitor.cs: public interface ITaxCalculator { void CalculateTax(ITaxVisitor visitor); } public interface ITaxVisitor { decimal ApplyTax(IncomeTax income); } We need to define an implementation for the host that will apply the Visitor Calculations. Let's call it TaxCalculator.cs: public class TaxCalculator : ITaxCalculator { private IncomeTax _incomeTax; public TaxCalculator(IncomeTax incomeTax) { _incomeTax = incomeTax; } public void CalculateTax(ITaxVisitor taxVisitor) { _incomeTax.Tax = taxVisitor.ApplyTax(_incomeTax); } public IncomeTax GetIncomeTax() { return _incomeTax; } } The CalculateTax method is responsible for applying visitor calculations. From here we can start creating our visitors, for the different "tax tiers". FirstTierTaxVisitor.cs public class FirstTierTaxVisitor : ITaxVisitor { public decimal ApplyTax(IncomeTax incomeTax) { if (incomeTax.Income > Constants.Tax.FIRST_TIER_INCOME) //$14,000.00
{
incomeTax.Tax = Constants.Tax.FIRST_TIER_INCOME * Constants.Tax.FIRST_TIER_RATE; // $14,000.00 * 10.5% } else { incomeTax.Tax = incomeTax.Income * Constants.Tax.FIRST_TIER_RATE; // Income provided * 10.5% } return incomeTax.Tax; } } SecondTierTaxVisitor.cs public class SecondTierTaxVisitor : ITaxVisitor { public decimal ApplyTax(IncomeTax incomeTax) { if (incomeTax.Income > Constants.Tax.SECOND_TIER_INCOME) //$48,000.00
{
incomeTax.Tax += (Constants.Tax.SECOND_TIER_INCOME - Constants.Tax.FIRST_TIER_INCOME) * Constants.Tax.SECOND_TIER_RATE; // ($48,000.00 -$14,000.00) * 17.5%
}
else if (incomeTax.Income is > Constants.Tax.FIRST_TIER_INCOME and <= Constants.Tax.SECOND_TIER_INCOME) // $14,000.00 < x <=$48,000.00
{
incomeTax.Tax += (incomeTax.Income - Constants.Tax.FIRST_TIER_INCOME) * Constants.Tax.SECOND_TIER_RATE; // (Income - $14,000.00) * 17.5% } return incomeTax.Tax; } } ThirdTierTaxVisitor.cs public class ThirdTierTaxVisitor : ITaxVisitor { public decimal ApplyTax(IncomeTax incomeTax) { if (incomeTax.Income > Constants.Tax.THIRD_TIER_INCOME) //$70,000.00
{
incomeTax.Tax += (Constants.Tax.THIRD_TIER_INCOME - Constants.Tax.SECOND_TIER_INCOME) * Constants.Tax.THIRD_TIER_RATE; // ($70,000.00 -$48,000.00) * 30%
}
else if (incomeTax.Income is > Constants.Tax.SECOND_TIER_INCOME and <= Constants.Tax.THIRD_TIER_INCOME) // $48,000.00 < x <=$70,000.00
{
incomeTax.Tax += (incomeTax.Income - Constants.Tax.SECOND_TIER_INCOME) * Constants.Tax.THIRD_TIER_RATE; // (Income - $48,000.00) * 30% } return incomeTax.Tax; } } FourthTierTaxVisitor.cs public class FourthTierTaxVisitor : ITaxVisitor { public decimal ApplyTax(IncomeTax incomeTax) { if (incomeTax.Income > Constants.Tax.FOURTH_TIER_INCOME) //$180,000.00
{
incomeTax.Tax += (Constants.Tax.FOURTH_TIER_INCOME - Constants.Tax.THIRD_TIER_INCOME) * Constants.Tax.FOURTH_TIER_RATE; // ($180,000.00 -$70,000.00) * 33%
}
else if (incomeTax.Income is > Constants.Tax.THIRD_TIER_INCOME and <= Constants.Tax.FOURTH_TIER_INCOME) // $70,000.00 < x <=$180,000.00
{
incomeTax.Tax += (incomeTax.Income - Constants.Tax.THIRD_TIER_INCOME) * Constants.Tax.FOURTH_TIER_RATE; // (Income - $70,000.00) * 33% } return incomeTax.Tax; } } FifthTierTaxVisitor.cs public class FifthTierTaxVisitor : ITaxVisitor { public decimal ApplyTax(IncomeTax incomeTax) { if (incomeTax.Income > Constants.Tax.FOURTH_TIER_INCOME) //$180,000.00
{
incomeTax.Tax += (incomeTax.Income - Constants.Tax.FOURTH_TIER_INCOME) * Constants.Tax.FIFTH_TIER_RATE; // (Income - $180,000.00) * 39% } return incomeTax.Tax; } } So we end up with Five (5) different visitors, that are calculating the Income Tax. ## Using the pattern Considering that now all the visitors were defined, they now can visit the host TaxCalculator, and then get our tax calculation: var incomeTax = new IncomeTax() { Income = 80000m }; var visitors = new List<ITaxVisitor> { new FirstTierTaxVisitor(), new SecondTierTaxVisitor(), new ThirdTierTaxVisitor(), new FourthTierTaxVisitor(), new FifthTierTaxVisitor() }; var taxCalculator = new TaxCalculator(incomeTax); foreach (var visitor in visitors) { taxCalculator.CalculateTax(visitor); } Console.WriteLine($"Income Tax: {taxCalculator.GetIncomeTax().Tax}");

In this example, you could see a practical (real-world) example of using visitors to solve a real-world problem. The example is available on PlayGoKids repository.