Java设计模式:深入理解访问者模式及其应用
这是文章《Java中的访问者设计模式》的第1部分(共1部分)。
访问者设计模式是行为型设计模式之一。
访问者设计模式详解
当我们需要对一组相似类型的对象执行操作时,可以考虑使用访问者模式。借助访问者模式,我们可以将操作逻辑从对象本身转移到另一个独立的类中。例如,想象一个购物车,我们可以添加不同类型的商品(元素)。当我们点击结算按钮时,它会计算需要支付的总金额。此时,我们可以选择将计算逻辑放在每个商品类中,也可以利用访问者模式将此逻辑转移到一个独立的计算类中。接下来,我们将通过一个具体的示例来演示如何在Java中实现这一功能。
访问者设计模式的Java示例
为了实现访问者模式,我们首先需要创建不同类型的商品(元素),以便在购物车中使用。定义一个接口 `ItemElement.java`:
package com.Olivia.design.visitor;
public interface ItemElement {
public int accept(ShoppingCartVisitor visitor);
}
请注意,`accept` 方法需要一个 `Visitor` 参数。为了简化示例,我们只关注访问者模式的核心概念,因此没有添加其他与商品相关的特定方法。现在,让我们为不同类型的商品创建具体的实现类。例如,`Book.java`:
package com.Olivia.design.visitor;
public class Book implements ItemElement {
private int price;
private String isbnNumber;
public Book(int cost, String isbn){
this.price=cost;
this.isbnNumber=isbn;
}
public int getPrice() {
return price;
}
public String getIsbnNumber() {
return isbnNumber;
}
@Override
public int accept(ShoppingCartVisitor visitor) {
return visitor.visit(this);
}
}
以及 `Fruit.java`:
package com.Olivia.design.visitor;
public class Fruit implements ItemElement {
private int pricePerKg;
private int weight;
private String name;
public Fruit(int priceKg, int wt, String nm){
this.pricePerKg=priceKg;
this.weight=wt;
this.name = nm;
}
public int getPricePerKg() {
return pricePerKg;
}
public int getWeight() {
return weight;
}
public String getName(){
return this.name;
}
@Override
public int accept(ShoppingCartVisitor visitor) {
return visitor.visit(this);
}
}
请注意,在具体的商品类中,`accept()` 方法的实现都调用了 `Visitor` 的 `visit()` 方法,并将自身作为参数传递。接下来,我们定义 `Visitor` 接口,其中包含针对不同类型商品定义的 `visit()` 方法,这些方法将由具体的访问者类来实现。定义 `ShoppingCartVisitor.java`:
package com.Olivia.design.visitor;
public interface ShoppingCartVisitor {
int visit(Book book);
int visit(Fruit fruit);
}
现在,我们将实现 `Visitor` 接口,并为每种商品编写其特定的成本计算逻辑。在 `ShoppingCartVisitorImpl.java` 文件中:
package com.Olivia.design.visitor;
public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {
@Override
public int visit(Book book) {
int cost=0;
// 如果图书价格大于50,则优惠5美元
if(book.getPrice() > 50){
cost = book.getPrice()-5;
}else cost = book.getPrice();
System.out.println("图书ISBN::"+book.getIsbnNumber() + " 价格 ="+cost);
return cost;
}
@Override
public int visit(Fruit fruit) {
int cost = fruit.getPricePerKg()*fruit.getWeight();
System.out.println(fruit.getName() + " 价格 = "+cost);
return cost;
}
}
让我们看看如何在客户端应用程序中使用这个访问者模式示例。通过 `ShoppingCartClient.java` 来实现:
package com.Olivia.design.visitor;
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);
}
private static int calculatePrice(ItemElement[] items) {
ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
int sum=0;
for(ItemElement item : items){
sum = sum + item.accept(visitor);
}
return sum;
}
}
当我们运行上述访问者模式客户端程序时,将得到以下输出结果:
图书ISBN::1234 价格 =20
图书ISBN::5678 价格 =95
香蕉 价格 = 20
苹果 价格 = 25
总成本 = 160
值得注意的是,所有商品中的 `accept()` 方法实现是相同的,但它也可以有所不同,例如可以加入逻辑来检查如果商品是免费的,则根本不调用 `visit()` 方法。
访问者设计模式类图
我们的访问者设计模式实现的类图如下:
[此处应插入类图图片]
访问者模式的优点
这种模式的优点在于,如果操作的逻辑发生变化,我们只需要在访问者实现中进行修改,而无需修改所有商品类。另一个优点是,向系统中添加新的商品非常容易,只需要在访问者接口和实现中进行修改,现有的商品类不会受到影响。
访问者模式的局限性
访问者模式的缺点是,我们在设计时需要知道 `visit()` 方法的返回类型,否则我们将不得不更改接口及其所有实现。另一个缺点是,如果访问者接口有太多实现,扩展将变得困难。关于访问者设计模式的内容就这些,如果我遗漏了什么,请告诉我。如果您喜欢这篇文章,请与他人分享。