行为模式之访问者模式
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 $93kg 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