
设计模式之访问者模式
什么是访问者模式
访客设计模式是行为设计模式之一。当我们必须对一组相似类型的对象执行操作时,将使用访问者模式。 借助访问者模式,我们可以将操作逻辑从对象移动到另一个类。
例如,考虑一个购物车,我们可以在其中添加不同类型的项目(元素)。 当我们点击结帐按钮时,它将计算要支付的总金额。 现在,我们可以将计算逻辑包含在项目类中,或者可以使用访问者模式将此逻辑移到另一个类中。 让我们在访问者模式示例中实现此功能。
访问者设计模式Java示例
为了实现访客模式,首先我们将创建要在购物车中使用的不同类型的项目(元素)。
ItemElement.java
1 2 3 4 5 6 7
|
public interface ItemElement { int accept(ShoppingCartVisitor visitor); }
|
请注意,accept()
方法采用Visitor
参数。 我们可以有一些其他的方法也特定于项目,但是为了简单起见,我并没有讨论太多细节,而只关注访问者模式。
让我们为不同类型的项目创建一些具体的类。
Book.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
public class Book implements ItemElement { private int price; private String isbnNumber;
public Book(int price, String isbnNumber) { this.price = price; this.isbnNumber = isbnNumber; }
public int getPrice() { return price; }
public String getIsbnNumber() { return isbnNumber; }
@Override public int accept(ShoppingCartVisitor visitor) { return visitor.visit(this); } }
|
Fruit.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
public class Fruit implements ItemElement { private int pricePerKg; private int weight; private String name;
public Fruit(int pricePerKg, int weight, String name) { this.pricePerKg = pricePerKg; this.weight = weight; this.name = name; }
public int getPricePerKg() { return pricePerKg; }
public int getWeight() { return weight; }
public String getName() { return name; }
@Override public int accept(ShoppingCartVisitor visitor) { return visitor.visit(this); } }
|
注意具体类中accept()
方法的实现,它调用Visitor
的visit()
方法并将其自身作为参数传递。
我们在Visitor
界面中有针对不同类型项目的visit()
方法,将由具体的访问者类实现。
ShoppingCartVisitor.java
1 2 3 4 5 6 7 8 9 10 11 12 13
|
public interface ShoppingCartVisitor {
int visit(ItemElement element); }
|
现在,我们将实现访问者,并且每个项目都有其自己的逻辑来计算费用。
ShoppingCartVisitorImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
public class ShoppingCartVisitorImpl implements ShoppingCartVisitor { @Override public int visit(ItemElement element) { int cost = 0; if (element instanceof Book) { Book book = (Book) element; if (book.getPrice() > 50) { cost = book.getPrice() - 5; } else { cost = book.getPrice(); } System.out.println("Book ISBN:: " + book.getIsbnNumber() + " cost = " + cost); } if (element instanceof Fruit) { Fruit fruit = (Fruit) element; cost = fruit.getPricePerKg() + fruit.getWeight(); System.out.println(fruit.getName() + " cost = " + cost); } return cost; } }
|
让我们看看如何在客户端应用程序中使用访问者模式示例。
ShoppingCartClient.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
public class ShoppingCartClient { public static void main(String[] args) { ItemElement[] items = new ItemElement[]{ new Book(20, "1234"), new Book(100, "5678"), new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple") };
int total = calculatePrice(items); System.out.println("Total Cost = " + total); }
private static int calculatePrice(ItemElement[] items) { ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl(); int sum = 0; for (ItemElement item : items) { sum += item.accept(visitor); } return sum; } }
|
当我们在访问者模式客户端程序之上运行时,我们得到以下输出。
1 2 3 4 5
| Book ISBN:: 1234 cost = 20 Book ISBN:: 5678 cost = 95 Banana cost = 12 Apple cost = 10 Total Cost = 137
|
请注意,所有项目中的accept()
方法的实现都是相同的,但可以有所不同,例如,可以使用逻辑检查项目是否可用,然后根本不调用visit()
方法。
访问者设计模式类图
我们的访问者设计模式实现的类图是:

访问者模式的好处
这种模式的好处是,如果操作逻辑发生变化,那么我们仅需要在访问者实现中进行更改,而无需在所有项目类中进行更改。
另一个好处是,将新项目添加到系统很容易,它仅需要在访问者界面和实现中进行更改,而现有项目类将不受影响。
访问者模式限制
访客模式的缺点在于,在设计时我们应该知道visit()
方法的返回类型,否则我们将不得不更改接口及其所有实现。 另一个缺点是,如果访问者接口的实现过多,则很难扩展。