何为访问者模式,从逛超市说起
这里使用课上的一个例子,对于超市的各个菜品,有着不同的计价规则,我们需要设计一种模式,让我们能保证在商品信息不发生大改动的情况下,可以对每个菜品采用不同的计价规则,并且这些规则可以轻易改变替换。
先假设一个商品总接口
public interface ItemElement {
public int accept(ShoppingCartVisitor visitor);
}
我们假设我们对每种商品有一个访问价格的访问者–购物卡,将价格的计算委托给购物卡来计算,便能实现如下几个商品类:
public class Book implements ItemElement {
private double price;
...
int accept(ShoppingCartVisitor visitor) {
return visitor.visit(this);
}
}
public class Fruit implements ItemElement {
private double weight;
...
int accept(ShoppingCartVisitor visitor) {
return visitor.visit(this);
}
}
对于每个商品,对于价值计算可以直接放手委托,不需要关心具体实现。
对于负责方法的Visitor类
public interface ShoppingCartVisitor {
int visit(Book book);
int visit(Fruit fruit);
}
public class ShoppingCartVisitorImpl implements ShoppingCartVisitor{
public int visit(Book book) {
int cost = 0;
if(book.getPrice() > 50) {
cost = book.getPrice()-5;
}
else
cost = book.getPrice();
System.out.println("Book OSBN::"+book.getIsbnNumber()+" coat ="+cost);
return cost;
}
public int visit(Fruit fruit) {
int cost = fruit.getPricePerKg()*fruit.getWeight();
System.out.println(fruit.getName()+" cost ="+cost);
return cost;
}
}
访问者类针对不同的商品重载不同的访问方法,对商品的计算依赖商品的属性;由此建立了商品与计费方法的双向委托。
访问者模式从本质上来说是一种委托,数据结构把接受函数委托给数据操作,操作函数用数据结构进行一系列操作。
访问者模式的本质结构
在逛完超市后,我们再把模式的本质结构从具体实现中抽象出来,可以看到主要是两个接口之间的相互委托:
具体结构正如上图所示,是element接口和Visitor接口的互相作用,一个只需简单的实现accept方法,另一个有针对性的实现visit方法即可。
选择恰当的应用场景
在实现visitor模式时我们就能意识到,对于数据接口,我们只需采用统一简单的accept即可,把繁琐的操作留给了visitor来实现。这便是visitor模式的实质,实现了数据结构和数据操作的分离。并且对于制定的数据结构,可以采用灵活的数据操作与之匹配。
这便是访问者模式的优势:符合单一职责原则,有优秀的扩展性,非常灵活性。
因此,对于对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作;亦或需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类的时候,我们便可以从容的使用visitor模式来灵活的构造不同的访问方法。