Showing posts with label visitor. Show all posts
Showing posts with label visitor. Show all posts

Thursday, 31 May 2018

Visitor Pattern

In the last post (please check that out before reading this, as it shows the problem we are trying to solve here) we saw how C# uses single-dispatch to select the methods to call. The argument type is determined at compile time and that is used to select the method out of many.

Here is an implementation of visitor pattern to resolve that problem:

void Main()
{
 Employee employee = new Employee();
 Employee manager = new Manager();

 IVisitor visitor = new SalaryAlgoPerformanceVisitor();
 
 employee.Accept(visitor);
 manager.Accept(visitor);
 
 Console.WriteLine(visitor.Output);
}

interface IVisitor
{
 string Output {get;}
 void Visit(Employee e);
 void Visit(Manager e);
}

class SalaryAlgoPerformanceVisitor : IVisitor
{
 public string Output { get; private set;}
 public void Visit(Employee e)
 {
  Output += $"Calculating salary of {nameof(Employee)} based on {nameof(SalaryAlgoPerformanceVisitor)}\n";
 }
 public void Visit(Manager e)
 {
  Output += $"Calculating salary of {nameof(Manager)} based on {nameof(SalaryAlgoPerformanceVisitor)}\n";
 }
}

class Employee
{
 public virtual string GetEmployeeType()
 {
  return nameof(Employee);
 }
 
 public virtual void Accept(IVisitor visitor)
 {
  visitor.Visit(this);
 }
}

class Manager : Employee
{
 public override string GetEmployeeType()
 {
  return nameof(Manager);
 }
 
 public override void Accept(IVisitor visitor)
 {
  visitor.Visit(this);
 }
}

Output:

Calculating salary of Employee based on SalaryAlgoPerformanceVisitor
Calculating salary of Manager based on SalaryAlgoPerformanceVisitor

You will notice that Accept method is resolved using the run time type of Employee object - this is the first dispatch. Then in the Accept method we use (this) so that appropriate method is called on the visitor - this is the second dispatch.

Since Accept is inside the class, we use the type of (this) to call the visitor.

Double Dispatch in C#

First few tests to understand the basics:

What do you think about the GetEmployeeType() method that will be called - will it be on Employee class or Manager class?

void Main()
{
 Employee e = new Manager();
 Console.WriteLine(e.GetEmployeeType()); 
}

class Employee
{
 public virtual string GetEmployeeType() {
  return nameof(Employee);
 }
}

class Manager : Employee
{
 public override string GetEmployeeType() {
  return nameof(Manager);
 }
}


Output: Manager

Since Employee object 'e' points to Manager, it will call the GetEmployeeType of Manager class.

Another one:

void Main()
{
 Employee employee = new Manager();
 Console.WriteLine(CalculateSalary(employee));
}

string CalculateSalary(Employee e)
{
 return $"{nameof(CalculateSalary)} of {nameof(Employee)}";
}

string CalculateSalary(Manager e)
{
 return $"{nameof(CalculateSalary)} of {nameof(Manager)}";
}

class Employee ...

class Manager : Employee ...


Output: CalculateSalary of Employee

Since the object is of type Employee the CalculateSalary accepting Employee will be called.


Taking this further, let's move the CalculateSalary to a class.

void Main()
{
 Employee employee = new Employee();
 Employee manager = new Manager();

 SalaryAlgo algo = new SalaryAlgoBasedOnPerformance();
 Console.WriteLine(algo.CalculateSalary(employee));
 Console.WriteLine(algo.CalculateSalary(manager));
}

class SalaryAlgo
{
 public virtual string CalculateSalary(Employee e)
 {
  return $"{nameof(CalculateSalary)} of {nameof(Employee)} based on {nameof(SalaryAlgo)}";
 }

 public virtual string CalculateSalary(Manager e)
 {
  return $"{nameof(CalculateSalary)} of {nameof(Manager)} based on {nameof(SalaryAlgo)}";
 }
}

class SalaryAlgoBasedOnPerformance : SalaryAlgo
{
 public override string CalculateSalary(Employee e)
 {
  return $"{nameof(CalculateSalary)} of {nameof(Employee)} based on {nameof(SalaryAlgoBasedOnPerformance)}";
 }

 public override string CalculateSalary(Manager e)
 {
  return $"{nameof(CalculateSalary)} of {nameof(Manager)} based on {nameof(SalaryAlgoBasedOnPerformance)}";
 }
}

class Employee ...

class Manager : Employee ...

Output:

CalculateSalary of Employee based on SalaryAlgoBasedOnPerformance
CalculateSalary of Employee based on SalaryAlgoBasedOnPerformance

As you can see, CalculateSalary(Manager) is not at all called. This is because the method to call is decided at compile time and the declared type is used to identify the method not the runtime type.

A quick way to fix this is to use "dynamic" keyword:

 Console.WriteLine(algo.CalculateSalary((dynamic)manager));

Output:

CalculateSalary of Employee based on SalaryAlgoBasedOnPerformance
CalculateSalary of Manager based on SalaryAlgoBasedOnPerformance

Another way to resolve this is to use Visitor pattern.

Helpful links:

https://blogs.msdn.microsoft.com/curth/2008/11/15/c-dynamic-and-multiple-dispatch/

EDIT: Post that implements Visitor pattern to resolve this problem.

Shorts - week 3, 2022

Post with links to what I am reading: 1. A very good post on different aspects of system architecture: https://lethain.com/introduction-to-a...