Visitor mode of behavior mode

Posted by markl999 on Sun, 31 May 2020 11:54:48 +0200

1 General

Visitor pattern is a behavior pattern, which is not commonly used. It can separate the algorithmic logic acting on the object from the object itself.

2 visitor mode

When we need to operate on a group of similar types of objects, we can maintain the operation logic within each object separately, but this violates the principle of single responsibility. The visitor pattern is to deal with this situation: move all the algorithm logic to a new class, visitor, and maintain it uniformly. If the logic changes, we only need to make changes in the visitor implementation without affecting the original object. At the same time, in the visitor mode, it is easy to expand, add new objects and operation logic, just add in the visitor.

3 cases

Let's take an example. In the process of shopping settlement, it is necessary to count the prices of all commodities, and at the same time take into account the discount of commodities. Different commodities have different preferential policies. We use visitor mode to unify the logic of settlement:

public interface Visitable {
    int accept(Visitor visitor);
}
public interface Fruit extends Visitable {
    int getPricePerKg();
    int getWeight();
}
// Fruits only need to maintain their own attributes such as unit price, weight and other information, and do not need to care about the settlement method
public class Apple implements Fruit {
    private int pricePerKg;
    private int weight;
    public Apple(int pricePerKg, int weight) {
        this.pricePerKg = pricePerKg;
        this.weight = weight;
    }
    [@Override](https://my.oschina.net/u/1162528)
    public int getPricePerKg() {
        return pricePerKg;
    }
    [@Override](https://my.oschina.net/u/1162528)
    public int getWeight() {
        return weight;
    }
    [@Override](https://my.oschina.net/u/1162528)
    public int accept(Visitor visitor) {
        return visitor.visit(this);
    }
}
public class Orange implements Fruit {
    private int pricePerKg;
    private int weight;
    public Orange(int pricePerKg, int weight) {
        this.pricePerKg = pricePerKg;
        this.weight = weight;
    }
    [@Override](https://my.oschina.net/u/1162528)
    public int getPricePerKg() {
        return pricePerKg;
    }
    [@Override](https://my.oschina.net/u/1162528)
    public int getWeight() {
        return weight;
    }
    @Override
    public int accept(Visitor visitor) {
        return visitor.visit(this);
    }
}
public class Banana implements Fruit {
    private int pricePerKg;
    private int weight;
    public Banana(int pricePerKg, int weight) {
        this.pricePerKg = pricePerKg;
        this.weight = weight;
    }
    @Override
    public int getPricePerKg() {
        return pricePerKg;
    }
    @Override
    public int getWeight() {
        return weight;
    }
    @Override
    public int accept(Visitor visitor) {
        return visitor.visit(this);
    }
}

public interface Visitor {
    int getTotalCost(Fruit... fruits);
    int visit(Apple apple);
    int visit(Orange orange);
    int visit(Banana banana);
}
// Visitor class maintenance specific algorithm logic
public class FruitVisitor implements Visitor {
    @Override
    public int getTotalCost(Fruit... fruits) {
        int cost = 0;
        for (Fruit fruit : fruits) {
            cost += fruit.accept(this);
        }
        return cost;
    }
    @Override
    public int visit(Apple apple) {
        // 20% off apple
        int pricePerKg = apple.getPricePerKg();
        if (pricePerKg > 10) {
            pricePerKg *= 0.8;
        }
        int cost = pricePerKg * apple.getWeight();
        System.out.println(apple.getWeight() + "kg apples costs $" + cost);
        return cost;
    }
    @Override
    public int visit(Orange orange) {
        // 2 kg orange, 2 yuan less
        int pricePerKg = orange.getPricePerKg();
        if (orange.getWeight() > 2) {
            pricePerKg -= pricePerKg - 2;
        }
        int cost = pricePerKg * orange.getWeight();
        System.out.println(orange.getWeight() + "kg oranges costs $" + cost);
        return cost;
    }
    @Override
    public int visit(Banana banana) {
        // There is no discount on bananas
        int cost = banana.getPricePerKg() * banana.getWeight();
        System.out.println(banana.getWeight() + "kg bananas costs $" + cost);
        return cost;
    }
}

Output:

1kg apples costs $9
3kg oranges costs $6
2kg bananas costs $16
Total cost: 31

By abstracting the logic of getTotalCost() from Fruit and maintaining it in a separate class Visitor, the code meets the principle of single responsibility. The modification of the discount algorithm will not affect the original Fruit object and achieve the goal of decoupling the object from the algorithm. In JDK, classes that end with Visitor generally use Visitor pattern, such as FileVisitorAnnotationValueVisitor...

4 Summary

Visitor pattern can separate the object from algorithm logic, and make the program easier to modify and expand. Of course, the disadvantage is that the implementation of the visitor interface is too much, which makes the visitor very complex. Although this mode is rare in practice, it can effectively reduce the complexity of the system in the appropriate scenario.

The github address of the example in this paper

Topics: Programming less JDK github