I have trouble understanding these two design patterns.
Can you please give me contextual information or an example so I can get a clear idea and be able to map the difference between the two of them.
Thanks.
A Visitor is a strategy but with multiple methods and it allows Double dispatch. The Visitor also allows for safe binding between two concrete objects at runtime.
Note: This is an example written in Java. For example C# introduced the dynamic
keyword, therefor the example of double dispatch is not useful in C#.
Consider the following example and the output:
package DesignPatterns;
public class CarGarageStrategyDemo
{
public static interface RepairStrategy
{
public void repair(Car car);
}
public static interface Car
{
public String getName();
public void repair(RepairStrategy repairStrategy);
}
public static class PorscheRepairStrategy implements RepairStrategy
{
@Override
public void repair(Car car) {
System.out.println("Repairing " + car.getName() + " with the Porsche repair strategy");
}
}
public static class FerrariRepairStrategy implements RepairStrategy
{
@Override
public void repair(Car car) {
System.out.println("Repairing " + car.getName() + " with the Ferrari repair strategy");
}
}
public static class Porsche implements Car
{
public String getName()
{
return "Porsche";
}
@Override
public void repair(RepairStrategy repairStrategy) {
repairStrategy.repair(this);
}
}
public static void main(String[] args)
{
Car porsche = new Porsche();
porsche.repair(new PorscheRepairStrategy()); //Repairing Porsche with the porsche repair strategy
}
}
The Strategy
pattern is working fine if there is no direct relationship between the strategy and the subject. For example, we don't want the following to happen:
...
public static void main(String[] args)
{
Car porsche = new Porsche();
porsche.repair(new FerrariRepairStrategy()); //We cannot repair a Porsche as a Ferrari!
}
...
So in this case we can use the visitor pattern.
Consider the code below:
public class CarGarageVisitorProblem
{
public static interface Car
{
public String getName();
}
public static class Porsche implements Car
{
public String getName()
{
return "Porsche";
}
}
public static class Ferrari implements Car
{
public String getName()
{
return "Ferrari";
}
}
public void repair(Car car)
{
System.out.println("Applying a very generic and abstract repair");
}
public void repair(Porsche car)
{
System.out.println("Applying a very specific Porsche repair");
}
public void repair(Ferrari car)
{
System.out.println("Applying a very specific Ferrari repair");
}
public static void main(String[] args)
{
CarGarageVisitorProblem garage = new CarGarageVisitorProblem();
Porsche porsche = new Porsche();
garage.repair(porsche); //Applying a very specific Porsche repair
}
}
The output is Applying a very specific Porsche repair
.
The problem is that this line is not abstract, but concrete:
Porsche porsche = new Porsche();
We want to write it as (or inject an instance of Car in the constructor, we want to apply the Dependency Inversion Principle
):
Car porsche = new Porsche();
But when we change this line, the output will be:
Applying a very generic and abstract repair
Not what we want!
package DesignPatterns;
public class CarGarageVisitorExample
{
public static interface Car
{
public String getName();
public void repair(RepairVisitorInterface repairVisitor);
}
public static class Porsche implements Car
{
public String getName()
{
return "Porsche";
}
public void repair(RepairVisitorInterface repairVisitor)
{
repairVisitor.repair(this);
}
}
public static class Ferrari implements Car
{
public String getName()
{
return "Ferrari";
}
public void repair(RepairVisitorInterface repairVisitor)
{
repairVisitor.repair(this);
}
}
public static interface RepairVisitorInterface
{
public void repair(Car car);
public void repair(Porsche car);
public void repair(Ferrari car);
}
public static class RepairVisitor implements RepairVisitorInterface
{
public void repair(Car car)
{
System.out.println("Applying a very generic and abstract repair");
}
public void repair(Porsche car)
{
System.out.println("Applying a very specific Porsche repair");
}
public void repair(Ferrari car)
{
System.out.println("Applying a very specific Ferrari repair");
}
}
public static void main(String[] args)
{
CarGarageVisitor garage = new CarGarageVisitor();
Car porsche = new Porsche();
porsche.repair(new RepairVisitor()); //Applying a very specific Porsche repair
}
}
Because of method overloading, there is a concrete binding between the visitor and the subject (Car). There is no way a Porsche can be repaired as a Ferrari, since it uses method overloading. Also we solved the previously explained problem (that we cannot use Dependency Inversion
), by implementing this method:
public void repair(RepairVisitorInterface repairVisitor)
{
repairVisitor.repair(this);
}
The this
reference will return the concrete type of the object, not the abstract (Car) type.