访问者模式的定义:将数据结构和对数据结构中元素的操作分离,并把对数据结构中元素的操作封装,使其在不改变数据结构的前提下可以添加新的操作,为数据结构中的每个元素提供多种访问方式。属于行为型模式。
访问者模式的核心是解耦数据结构和数据操作,使得对元素的操作更容易扩展。
访问者模式的结构:访问者模式主要包含以下5个角色。
- 抽象访问者(Visitor):定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数就是具体元素对象。
- 具体访问者(ConcreteVisitor):实现抽象访问者中的方法,实现元素的具体操作。
- 抽象元素(Element):定义一个包含接受操作 accept() 的接口或抽象类,被接受的访问者对象作为 accept() 方法的参数。
- 具体元素(ConcreteElement)角色:实现抽象元素提供的 accept() 操作,其方法体通常都是 visitor.visit(this)。
- 对象结构(Object Structure):包含元素的容器,提供让访问者对象遍历容器中的所有元素的方法。
访问者模式的通用实现:
//抽象元素
public interface IElement {
void accept(IVisitor visitor);
}
//具体元素
public class ConcreteElementA implements IElement{
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
public String operationA() {
return "对元素A一顿操作!";
}
}
//具体元素
public class ConcreteElementB implements IElement{
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
public String operationB() {
return "对元素B一顿操作!";
}
}
//对象结构
public class ObjectStructure {
private List<IElement> list = new ArrayList<>();
public void accept(IVisitor visitor) {
Iterator<IElement> iterator = list.iterator();
while (iterator.hasNext()) {
iterator.next().accept(visitor);
}
}
public void add(IElement element) {
list.add(element);
}
public void remove(IElement element) {
list.remove(element);
}
}
//抽象访问者
public interface IVisitor {
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
//具体访问者
public class ConcreteVisitorA implements IVisitor {
@Override
public void visit(ConcreteElementA concreteElementA) {
System.out.println("具体访问者A访问-->" + concreteElementA.operationA());
}
@Override
public void visit(ConcreteElementB concreteElementB) {
System.out.println("具体访问者A访问-->" + concreteElementB.operationB());
}
}
//具体访问者
public class ConcreteVisitorB implements IVisitor {
@Override
public void visit(ConcreteElementA concreteElementA) {
System.out.println("具体访问者B访问-->" + concreteElementA.operationA());
}
@Override
public void visit(ConcreteElementB concreteElementB) {
System.out.println("具体访问者B访问-->" + concreteElementB.operationB());
}
}
//测试类
public class VisitorTest {
public static void main(String[] args) {
ObjectStructure os = new ObjectStructure();
os.add(new ConcreteElementA());
os.add(new ConcreteElementB());
IVisitor visitor = new ConcreteVisitorA();
os.accept(visitor);
System.out.println("========================================");
visitor = new ConcreteVisitorB();
os.accept(visitor);
}
}
访问者模式的结构图:
访问者模式的应用实例:顾客在商场购物时将选购的商品放在“购物车”中,顾客主要关心所选商品的性价比,而收银员关心的是商品的价格和数量。我们用访问者模式来模拟一下这个场景。
//抽象商品
public abstract class Goods {
private String name;
private Double price;
private Double amount;
public Goods(String name,Double price,Double amount){
this.name = name;
this.price = price;
this.amount = amount;
}
public String getName() {
return name;
}
public Double getPrice() {
return price;
}
public Double getAmount() {
return amount;
}
public abstract void accept(IVisitor visitor);
}
//具体商品
public class Apple extends Goods{
public Apple(String name, Double price, Double amount) {
super(name, price, amount);
}
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
public String getCost(){
return "好吃不贵,易于保存。";
}
}
//具体商品
public class Banana extends Goods{
public Banana(String name, Double price, Double amount) {
super(name, price, amount);
}
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
public String getCost(){
return "好吃,不易保存。";
}
}
//访问者接口
public interface IVisitor {
void visit(Apple apple);
void visit(Banana banana);
}
//顾客(访问者)
public class Customer implements IVisitor{
@Override
public void visit(Apple apple) {
System.out.println("苹果:单价是"+apple.getPrice()+",特点是"+apple.getCost());
}
@Override
public void visit(Banana banana) {
System.out.println("香蕉:单价是"+banana.getPrice()+",特点是"+banana.getCost());
}
}
//商家(访问者)
public class Saler implements IVisitor{
@Override
public void visit(Apple apple) {
System.out.println("苹果:单价是"+apple.getPrice()+",数量是"+apple.getAmount());
}
@Override
public void visit(Banana banana) {
System.out.println("香蕉:单价是"+banana.getPrice()+",数量是"+banana.getAmount());
}
}
//购物车
public class ShoppingCart {
static List<Goods> goods = new ArrayList<>();
static {
goods.add(new Apple("红富士苹果",4.00,3.00));
goods.add(new Apple("花牛苹果",6.00,2.00));
goods.add(new Apple("国产香蕉",5.00,2.00));
goods.add(new Apple("进口香蕉",8.00,2.00));
}
public void showGoods(IVisitor visitor){
for (Goods good : goods) {
good.accept(visitor);
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
ShoppingCart shoppingCart = new ShoppingCart();
System.out.println("==============顾客看商品================");
shoppingCart.showGoods(new Customer());
System.out.println("==============商家看商品================");
shoppingCart.showGoods(new Saler());
}
}
访问者模式的优点:
- 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
- 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
- 灵活性好。访问者模式将数据结构与数据操作解耦,使得操作集合可相对自由地变化。
- 符合单一职责原则。访问者模式把相关的行为封装在一起,每一个访问者的功能都比较单一。
访问者模式的缺点:
- 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
- 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。
- 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
访问者模式的应用场景:
- 对象结构相对稳定,但其操作算法经常变化的场景。
- 数据对象与数据操作分离的场景。