典型应用场景
一个集合容器,里面有一系列元素,但是不同的元素类型不太一样,例如A,B,C,D,但是不同的元素肯定继承自同一个父类或者实现同一个接口,不妨设为Element,毕竟这样才能在同一个集合数据结构里面。现在你想要遍历这个集合,譬如你想得到每个元素的价格,然后求个总价,自然想到的方法是为Element加一个getPrice()方法,然后每个子类都实现这个方法,每个子类可以根据自己的情况选择不同的实现。最终达到求总价的目的。
存在问题
上面解决了求总价的问题,但是如果你哪天又想要求总数量呢?获取每个元素的重量以求得最大的重量呢?用上面的方法,必须为Element再加一个getWeight()亦或是getNum()方法。这必须修改类内代码,对于一些坚持OCP原则的人来说,这不能忍。所以它们想出了Visitor Pattern。
类图
实例代码
本例是一个元素集合,集合里面有蔬菜、书,现在要求求总价,所以有了个PriceVisitor,如果你还要得到重量,也可以添加WeightVisitor. 有几个操作就是几个Visitor。不需要修改原有代码,只需要添加具体Visitor类。
Element:
public abstract class Element {
abstract double accept(Visitor visitor);
}
Book:
public class Book extends Element{
private String ISBN;
public Book(String ISBN) {
this.ISBN = ISBN;
}
public String getISBN() {
return ISBN;
}
@Override
double accept(Visitor visitor) {
return visitor.visitBook(this);
}
}
Fruit:
public class Fruit extends Element {
private double num;
private double price;
public Fruit(double num,double price) {
this.num = num;
this.price=price;
}
public double getPrice() {
return price;
}
public double getNum() {
return num;
}
@Override
double accept(Visitor visitor) {
return visitor.visitFruit(this);
}
}
Visitor:
public interface Visitor {
double visitFruit(Fruit fruit);
double visitBook(Book book);
}
PriceVisitor:
public class PriceVisitor implements Visitor {
@Override
public double visitFruit(Fruit fruit) {
return fruit.getPrice()*fruit.getNum();
}
@Override
public double visitBook(Book book) {
String ISBN=book.getISBN();
if(ISBN.charAt(0)=='a')
return 100;
else
return 200;
}
}
Client:
public class Client {
public static void main(String[] args){
List<Element> list=new ArrayList<>();
list.add(new Book("a9148210"));
list.add(new Fruit(20,2));
list.add(new Book("b2213"));
list.add(new Fruit(10,3));
Visitor visitor=new PriceVisitor();
double sum=0;
for(Element element:list){
sum+=element.accept(visitor);
}
System.out.print(sum);
}
}