What is the difference between Strategy pattern and Visitor Pattern?

后端 未结 11 1494
灰色年华
灰色年华 2021-01-29 22:18

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 dif

11条回答
  •  傲寒
    傲寒 (楼主)
    2021-01-29 22:36

    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#.

    Strategy pattern

    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.

    Visitor

    The problem

    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!

    The solution; using double dispatch (and the Visitor pattern)

    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.

提交回复
热议问题