访问者模式
访问者模式是一种行为型设计模式,它允许你将对象操作(算法)与对象结构分离。可以在不更改对象的类的情况下,向现有对象结构添加新操作。
应用场景
访问者模式适用于以下场景:
- 当你需要在一些类群中定义新的操作,但不希望代码频繁更改这些类群时,可以使用访问者模式。
- 如果您需要对已有对象结构进行复杂的结构化操作,可能需要该模式。
- 该模式可以帮助在不改变对象结构的情况下向该结构中添加新功能。
结构
访问者模式包括以下组件:
- Visitor: 表示访问者的抽象类或接口。需要为对象结构中每个类群实现访问者操作。
- ConcreteVisitor: 具体的访问者实现,实现 Visitor 接口并在每个类群中提供具体操作。
- Element: 所有对象结构的基类,必须有一个 Accept 方法。该方法需要将访问者作为参数,以便处理程序可以在 Visitor 调用期间调用 Element 特定的操作。
- ConcreteElement: 表示具体元素的类。它们必须实现 Accept 并将自身作为参数传递给访问者,以便访问者可以遍历元素并调用特定于该元素类型的操作。
- ObjectStructure: 表示对象结构的类。它需要有一个数据结构来存储元素,并能够将访问者传递给所有元素,以便访问者可以遍历整个对象结构。
代码示例
下面是一个使用访问者模式来计算购物车中商品价格的示例。假设我们购物车中有书籍和电影两种商品,我们需要计算它们的价格并返回结果。
首先,我们需要定义访问者接口
public interface Visitor {
double visit(Book book);
double visit(Movie movie);
}
然后,我们需要定义商品基类 Element 和具体商品子类
public interface Element {
double accept(Visitor visitor);
}
public class Book implements Element {
private String name;
private double price;
private double weight;
public Book(String name, double price, double weight) {
this.name = name;
this.price = price;
this.weight = weight;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
public double getWeight() {
return weight;
}
@Override
public double accept(Visitor visitor) {
return visitor.visit(this);
}
}
public class Movie implements Element {
private String name;
private double price;
private int length;
public Movie(String name, double price, int length) {
this.name = name;
this.price = price;
this.length = length;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
public int getLength() {
return length;
}
@Override
public double accept(Visitor visitor) {
return visitor.visit(this);
}
}
现在我们需要实现具体的访问者类 ConcreteVisitor
public class PriceVisitor implements Visitor {
@Override
public double visit(Book book) {
return book.getPrice();
}
@Override
public double visit(Movie movie) {
return movie.getPrice() * movie.getLength() / 60.0;
}
}
最后,我们需要定义 ObjectStructure
public class ShoppingCart {
private List<Element> elements;
public ShoppingCart() {
elements = new ArrayList<>();
}
public void addElement(Element element) {
elements.add(element);
}
public void removeElement(Element element) {
elements.remove(element);
}
public double calculateTotalPrice(Visitor visitor) {
double totalPrice = 0.0;
for (Element element : elements) {
totalPrice += element.accept(visitor);
}
return totalPrice;
}
}
现在我们可以使用访问者模式来计算购物车中商品价格:
public static void main(String[] args) {
ShoppingCart shoppingCart = new ShoppingCart();
shoppingCart.addElement(new Book("Design Patterns", 40.0, 1.5));
shoppingCart.addElement(new Movie("The Lord of the Rings", 20.0, 180));
shoppingCart.addElement(new Book("Effective Java", 50.0, 1.0));
shoppingCart.addElement(new Movie("Inception", 15.0, 120));
Visitor visitor = new PriceVisitor();
double totalPrice = shoppingCart.calculateTotalPrice(visitor);
System.out.println("Total Price: $" + totalPrice);
}
输出结果:
Total Price: $110.0
优缺点
优点:
- 可以对已有对象结构进行复杂的结构化操作,而无需修改这些对象结构。
- 可以使用具体访问者来添加新的操作,而不必更改现有代码。
- 可以将相关操作代码集中到一起,而不是分散在整个程序中。
- 可以简化算法代码,使其易于扩展和维护。
- 可以让客户端代码和元素类保持相对不变。
缺点:
- 如果元素类层次结构经常更改,则访问者模式可能会变得很麻烦。
- 在使用访问者模式时添加新元素可能比添加新操作更困难。
- 访问者模式可能会导致对象结构中的许多小的类。
- 具体的访问者可能需要访问元素的私有成员,这可能会破坏元素的封装。
总结
访问者模式强调了“单一职责原则”和“开放/封闭原则”,它提供了一种方便的方式来添加新操作,而无需更改现有代码。它适用于对象结构相对稳定的场景,并且可用于执行诸如优化、验证、反向操作等任务。它不应被滥用,因为在某些情况下,它可能会增加系统的复杂性。