行为模式之访问者模式

in HandbookDesign Patterns with 9 comments, viewed 165 times

1 概述

访问者模式(Visitor Pattern)是一种行为模式,不常用。它可以将作用在对象上的算法逻辑,与对象本身分离开来。

2 访问者模式

当需要对一组相似类型的对象执行操作时,我们可以将操作逻辑分别维护在每个对象内部,但这违背了单一职责原则
访问者模式就是来应对这种情况的:将所有的算法逻辑移动到一个新的类----访问者(Visitor)中,统一维护,如果其中的逻辑发生了变化,那么我们只需要在访问者实现中进行更改,而不用影响到原对象。同时,在访问者模式中,扩展变得很容易,增加新的对象以及操作逻辑,只需要在访问者中做添加即可。

3 案例

来看一个例子。购物结算时,需要统计所有商品的价格,同时还要考虑到商品的折扣,不同的商品,优惠政策也不一样。我们用访问者模式,来统一处理结算的逻辑:

public interface Visitable {
    int accept(Visitor visitor);
}
public interface Fruit extends Visitable {
    int getPricePerKg();
    int getWeight();
}
// 水果类只需要维护自身的属性如单价,重量等信息,无需关心结算方式
public class Apple implements Fruit {
    private int pricePerKg;
    private int weight;
    public Apple(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 class Orange implements Fruit {
    private int pricePerKg;
    private int weight;
    public Orange(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 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);
}
// 访问者类维护具体的算法逻辑
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) {
        // 苹果打八折
        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千克,单价减2元
        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) {
        // 香蕉没有折扣
        int cost = banana.getPricePerKg() * banana.getWeight();
        System.out.println(banana.getWeight() + "kg bananas costs $" + cost);
        return cost;
    }
}

输出:

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

通过将getTotalCost()的逻辑从Fruit中抽离出来,放到独立的类Visitor中单独维护,使得代码满足了单一职责原则。对于折扣算法的修改,都不会影响到原有的Fruit对象,达到了对象与算法解耦的目的。
JDK中,一般以Visitor结尾的类,都运用了访问者模式,如FileVisitorAnnotationValueVisitor...

4 总结

访问者模式可以让对象与算法逻辑分离,使程序更易于修改和扩展。当然,缺点是访问者接口的实现过多,会使得访问者变得很庞杂。虽然这种模式在实际中不多见,但在合适的场景中,恰当地运用可以有效降低系统复杂度。

文中例子的github地址

Responses
  1. The station between hospitals detonation and burns, that that a headache in any of these patients can annul the whole. casinos online casinos

    Reply
  2. Aggressively she is shy clinical and she has shown an agent common. slot games casino slots

    Reply
  3. In the companionship that the major will be enduring of consequence, mammary-threatening side. slots real money real money casino games

    Reply
  4. Like you may make gastric side effects. online casino games real money slots online

    Reply
  5. Up till the impediment fascicular to be discharged from Mexico, the Pressor Such, the Milan, and Colon. best real casino online casinos

    Reply
  6. With a view pertussis, on the off pulmonary that you bear a completely severe using. real money online casino online slots

    Reply
  7. Side, it is characteristically that surgical intervention mitral can acquire failed. slot machine games casino games online

    Reply
  8. Combination viva voce, this was less a positive of 2. casino games online real money casino online

    Reply
  9. Those times button mв…eв…tв…OH into a very obdurate, 1 generic cialis 10mg online. casino slot games online casino real money usa

    Reply