We have a project where goals are changing all the time
In this article I will try to introduce Visitor pattern and one possible variation on it. It treats problems related to projects where high level of adaptability is needed. In the course of project development, especially if we are talking about a long-lasting project, we may expect that changes will be introduced, which will require redesigning app completely or introducing some way of handling changes in a less destructive manner. Well known OO recipe is Visitor pattern and well known procedural solution is to cast Type an write few additional lines of code or functions. I will try to explain that using a small example where salary is calculated for employees of some imaginary company. At the beginning of that company it was sufficient to sum salaries of employees to find out what is monthly expense.
Here is the code :
using
System;
public abstract class Employee
{
protected double _salary;
public Employee(double salary)
{
_salary=salary;
}
public abstract void DoWork();
public double salary
{
get
{
return _salary;
}
}
}
public class JuniorEmployee:Employee
{
public JuniorEmployee(double salary):base(salary){}
public override void DoWork()
{
Console.WriteLine("OK I'm doing junior employee work.");
}
}
public class SeniorEmployee:JuniorEmployee
{
public SeniorEmployee(double salary):base(salary){}
public override void DoWork()
{
Console.WriteLine("OK I'm doing senior employee work.");
}
}
class driver
{
static void Main()
{
JuniorEmployee j=new JuniorEmployee(3000);
SeniorEmployee s=new SeniorEmployee(5000);
Console.WriteLine("Pay to junior : {0}\nPay to senior : {1}\nTotal : {2}",j.salary,s.salary,j.salary+s.salary);
}
}
Accounting department will use more sophisticated version of this and they will just type in amounts for employees, or they will just connect to SQL to make app read amounts. Unfortunately, a new tax law was introduced and company, according to that law, has to pay tax of 25% on salaries, so to calculate monthly expenses it was necessary to add more code. That was simple, we will insert in Main the following :
double
Total = (j.salary+s.salary) * 1.25;
Another work-related law came into power which required to pay medical benefits to all employees, and related tax law which says that the tax on medical benefits is different than on the rest of the salary. Now we are already in a bit of trouble. We can nicely forget about procedural approach and start introducing derived employee which will take care about extras and tax rate. But that wasnt all, the company started using contractors which are paid per hour and they do not receive additional medical benefits. On top of that the legislator decided that tax on lower salaries should be smaller and that the company must pay some percentage to retirement fund. We can proceed deriving, but the legislator will proceed to change laws and we will find ourselves in one endless job. Since the company has no money to finance such never-ending project they will probably tell accounting department to do that manually and we developers may start looking for another project. What have we developers tried to do so far?
We came up with hierarchy of classes similar to this one :
public abstract class taxableEmployeeplusExtras:Employee
{
protected double _tax;
protected double _extras;
public taxableEmployeeplusExtras(double amt, double taxrate):base(amt)
{
_tax+=amt*taxrate;
}
public void AddExtras(double amt, double taxrate)
{
_extras+=amt*taxrate;
}
public double TAX
{
get
{
return _tax;
}
}
public double Extras
{
get
{
return _extras;
}
}
}
For contactors we have to write separate class :
public
class Contractor
{
double _rate;
double _h;
public Contractor(double rate)
{
_rate=rate;
}
public double Hours
{
get
{
return _h;
}
set
{
_h=value;
}
}
public double GetSalary()
{
return _rate*_h;
}
public double GetTax(double taxrate)
{
return _rate*_h*taxrate;
}
}
To make contactor and employee polymorphic we decided to change read only properties of employee to methods and introduced interface:
public interface ITaxable
{
double GetSalary();
double GetTax(double taxrate);
}
So we changed base class for employees and now we can use ITaxable as parameter and cast it to what is needed. In that way we nicely mixed OO and procedural approach, which guarantees problems in the future. Sooner or later another type of employee will appear or new law will be introduced and we will start from the beginning.
Visitor Pattern
This pattern is a solution for situations where we should perform some operations on group of objects which originally dont support that kind of operations. Visitor will access data of some object (if data is accessible) and perform these additional operations. To implement that in our case we may do the following : we will declare interface IVisitable and derive from employee or contractor base class implementing that interface, also we will write visitor which will do work on these two. But first we must change classes to make data available to visitors.
public abstract class Employee
{
double _salary;
public abstract void DoWork();
public double salary
{
get
{
return _salary;
}
set
{
_salary=value;
}
}
}
public class Contractor
{
double _rate;
double _h;
public double Hours
{
get
{
return _h;
}
set
{
_h=value;
}
}
public double Rate
{
get
{
return _rate;
}
set
{
_rate=value;
}
}
}
Now these two just hold data and we may be sure that we wont have to change that. To accept visitors we will use derived classes and implement IVisitable.
public interface IVisitable
{
void Accept(Visitor v);
}
public abstract class VisitableEmployee: Employee, IVisitable
{
public void Accept(Visitor v)
{
v.Visit(this);
}
}
public class VisitableContractor: Contractor, IVisitable
{
public void Accept(Visitor v)
{
v.Visit(this);
}
}
Visitor will look like this :
public interface IVisitor
{
void Visit(VisitableEmployee ve);
void Visit(VisitableContractor vc);
}
class Visitor:IVisitor
{
public void Visit(VisitableEmployee ve)
{
// Do work on employee.
}
public void Visit(VisitableContractor vc)
{
// Do work on contractor.
}
}
Complete example for calculating tax on salary will look like this :
using System;
public abstract class Employee
{
double _salary;
public abstract void DoWork();
public double salary
{
get
{
return _salary;
}
set
{
_salary=value;
}
}
}
public class Contractor
{
double _rate;
double _h;
public double Hours
{
get
{
return _h;
}
set
{
_h=value;
}
}
public double Rate
{
get
{
return _rate;
}
set
{
_rate=value;
}
}
}
public interface IVisitable
{
void Accept(Visitor v);
}
public abstract class VisitableEmployee:Employee,IVisitable
{
public void Accept(Visitor v)
{
v.Visit(this);
}
}
public class VisitableContractor:Contractor,IVisitable
{
public void Accept(Visitor v)
{
v.Visit(this);
}
}
public interface IVisitor
{
void Visit(VisitableEmployee ve);
void Visit(VisitableContractor vc);
}
public class Visitor:IVisitor
{
double _taxrate;
protected double _tax;
public double TaxRate
{
get
{
return _taxrate;
}
set
{
_taxrate=value;
}
}
public void Visit(VisitableEmployee ve)
{
_tax+=ve.salary*_taxrate;
}
public void Visit(VisitableContractor vc)
{
_tax+=vc.Hours*vc.Rate*_taxrate;
}
public double TotalTax()
{
return _tax;
}
}
public class JuniorEmployee:VisitableEmployee
{
public override void DoWork()
{
Console.WriteLine("OK I'm doing junior employee work.");
}
}
class driver
{
static void Main()
{
JuniorEmployee j=new JuniorEmployee();
j.salary=2500;
VisitableContractor c=new VisitableContractor();
c.Rate=50;
c.Hours=120;
Visitor v=new Visitor();
v.TaxRate=0.25;
j.Accept(v);
c.Accept(v);
Console.WriteLine("Salary for :\nJunior : {0}\nContractor {1}",j.salary,c.Rate*c.Hours);
Console.WriteLine("Tax : {0}",v.TotalTax());
}
}
That does the job and we are happy. Tax calculation is kept externally, and if tomorrow it is not needed we can keep our objects and use them as they are. I wont bother you with double dispatching and further analysis of Visitor, for further information, you can read "Design Patterns", p331, Gamma et al., Addison-Wesley, ISBN:0-201-63361-2.
Trouble with Visitor
Now when we must add medical benefits we will inherit from old Visitor and in that way reuse already existing visitor :
public class Visitor2:Visitor
{
double _medical;
double _medicaltax;
public double Medical
{
get
{
return _medical;
}
set
{
_medical=value;
}
}
public double MedicalTax
{
get
{
return _medicaltax;
}
set
{
_medicaltax=value;
}
}
new public void Visit(VisitableEmployee ve)
{
base.Visit(ve);
ve.salary+=_medical;
_tax+=_medicaltax*_medical;
}
}
So for contractors base class will be called, and for employees the derived one. Unfortunately that is not all. We will also have to change VisitableEmployee. Accept looks like this :
public void Accept(Visitor v)
{
v.Visit(this);
}
That will have as the result that base visitor is called instead of derived, and unlucky junior will stay short of medical benefits. Now we again have the possibility to cast parameter v to Visitor2 and make all that work.
public void Accept(Visitor v)
{
((Visitor2)v).Visit(this);
}
Code inside Main will look like this :
JuniorEmployee j=new JuniorEmployee();
j.salary=2500;
VisitableContractor c=new VisitableContractor();
c.Rate=50;
c.Hours=120;
Visitor2 v=new Visitor2();
v.TaxRate=0.25;
v.Medical=250;
v.MedicalTax=0.05;
j.Accept(v);
c.Accept(v);
Console.WriteLine("Salary for :\nJunior : {0}\nContractor : {1}",j.salary,c.Rate*c.Hours);
Console.WriteLine("Tax : {0}",v.TotalTax());
Lot of OO people will say that to do casting is not acceptable. What else can we do? We can introduce IVisitable2 instead of IVisitable or give up on hierarchy of visitors all together and rewrite visitor every time from scratch. For me the possibility to reuse already existing code looks very OO, so I will gladly accept non OO hack to make OO strategy work. In the long run procedural strategies will not work if system is going through changes; here Im following OO strategy and I dont expect problems. What will happen if another kind of employee is introduced, e.g. temp? Again we have to start from scratch. Visitor introduces cyclic dependency, visitable objects must know visitors and the other way round. What if we go for even more casting and base everything on two interfaces? For example :
public interface IVisitor
{
void Visit(IVisitable v);
}
public interface IVisitable
{
void Accept(IVisitor v);
}
Now Accept may dynamically cast parameter v to real type of caller. Casting problem wont be solved because we are using interface. If we dont do casting base visitor will receive all calls. Normally we may use virtual and override to solve this problem. Automating Accept via reflection is very simple, but take into account possible performance penalty. Even if parameter is declared as interface it is a reference to object.
public void Accept(IVisitor v)
{
v.GetType().InvokeMember ("Visit", BindingFlags.InvokeMethod, null, v, new object [] {this});
}
Dont forget to add reference to System.Reflection. If speed is an issue rather go for virtual and override or stick with ordinary casting and document what you are casting and why, appropriate versioning and usage of custom attributes may be very useful here. Situation with visitors is more complicated. We have few options, the simplest is to use one hierarchy of visitors for each type of visitable object. For example :
public class BaseVisitor
{
protected double _taxrate;
protected double _tax;
public double TaxRate
{
get
{
return _taxrate;
}
set
{
_taxrate=value;
}
}
public double TotalTax()
{
return _tax;
}
}
public class EmployeeVisitor:BaseVisitor, IVisitor
{
public void Visit(IVisitable ve)
{
_tax+=((VisitableEmployee)ve).salary*_taxrate;
}
}
public class ContractorVisitor:BaseVisitor,IVisitor
{
public void Visit(IVisitable vc)
{
_tax+=((VisitableContractor)vc).Hours*((VisitableContractor)vc).Rate*_taxrate;
}
}
public class MedicalVisitor:EmployeeVisitor
{
double _medical;
double _medicaltax;
public double Medical
{
get
{
return _medical;
}
set
{
_medical=value;
}
}
public double MedicalTax
{
get
{
return _medicaltax;
}
set
{
_medicaltax=value;
}
}
new public void Visit(IVisitable ve)
{
base.Visit(ve);
((VisitableEmployee)ve).salary+=_medical;
_tax+=_medicaltax*_medical;
}
}
Complete code for this version is in attachment. The fact that we are keeping externally all tax calculations and that we are capable of build up on visitor hierarchy says that modified Visitor pattern works. Also we may conclude that sometimes heretic ideas like casting in OO pattern may be acceptable. I didnt really test it for performance, so dont ask me that; on array of 1000 of each with one additional visitor in hierarchy I didnt note any delays. My opinion is that software development not based on OO principles actually represents mockery of software development. With due respect to people which are using MSSQL server or Oracle or maybe MASM and plain C to make a living, writing SQL queries or device driver is not real programming.