一、简介

1.1 模式定义

表示一个作用于某个对象结构中某个元素的操作,它使我们可以在不改变某个元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种行为型模式。

1.2 适用场景

1)一个对象结构包含很多类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。
2)需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让给这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。访问者模式使得我们可以将相关的访问操作集中起来定义访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。
3)对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。

1.3 优点

1)使得增加新的访问操作变得很容易。使用访问者模式,增加新的访问操作就意味着增加一个新的访问者类,无须修改现有类库代码,符合“开闭原则”的要求。
2)将有关元素对象的访问行为集中到一个访问者对象中,而不是分散到一个个的元素类中。类的职责更加清晰,有利于对象结构中元素对象的复用,相同的对象结构可以供多个不同的访问者访问。
3)可以跨过类的等级结构访问属于不同的等级结构的元素类。
4)让用户能够在不修改现有类层次结构的情况下,定义该类层次结构的新操作。

1.4 缺点

1)增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求。
2)破坏封装。访问者模式要求访问者对象访问并调用没一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法提供访问者访问。

二、示例:购物车

2.1 结构图


2.2 抽象访问者Visitor(访问者类)
public abstract class Visitor {

	protected String name;
	
	public void setName(String name) {
		this.name = name;
	}

	public abstract void visit(Apple apple);
	
	public abstract void visit(Book book);
	
}
2.3 具体访问者Customer(顾客类)
public class Customer extends Visitor{

	@Override
	public void visit(Apple apple) {
		System.out.println("顾客" + name + "选择苹果。");
	}

	@Override
	public void visit(Book book) {
		System.out.println("顾客" + name + "买书。");
	}

}
2.4 具体访问者类Saler(收银员类)
public class Saler extends Visitor{

	@Override
	public void visit(Apple apple) {
		System.out.println("收银员" + name + "给苹果过秤,然后计算价格。");
	}

	@Override
	public void visit(Book book) {
		System.out.println("收银员" + name + "直接计算书的价格");
	}

}
2.5 抽象元素类Product(商品类)
public interface Product {
	
	public void accept(Visitor visitor);
	
}
2.6 具体元素类Apple(苹果类)
public class Apple implements Product{

	@Override
	public void accept(Visitor visitor) {
		visitor.visit(this);
	}

}
2.7 具体元素类Book(书籍类)
public class Book implements Product{

	@Override
	public void accept(Visitor visitor) {
		visitor.visit(this);
	}

}
2.8 对象结构BuyBasket(购物车类)
public class BuyBasket {
	
	private List<Product> list = new ArrayList<>();
	
	public void accept(Visitor visitor) {
		Iterator<Product> i = list.iterator();
		while(i.hasNext()) {
			i.next().accept(visitor);
		}
	}
	
	public void add(Product product) {
		list.add(product);
	}

	public void remove(Product product) {
		list.remove(product);
	}
	
}