行为模式之访问者模式

编程

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](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);

}

// 访问者类维护具体的算法逻辑

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结尾的类,都运用了访问者模式,如FileVisitor,AnnotationValueVisitor...

4 总结

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

文中例子的github地址

以上是 行为模式之访问者模式 的全部内容, 来源链接: utcz.com/z/516983.html

回到顶部