访问者模式
在处理某些容器类对象结构的时候,对象结构中存储了多种不同的对象信息,并且针对元素的处理方式并不唯一,可能需要提供多种不同的处理方式,也有可能增加新的处理方式。对于这种情景,可以考虑使用访问者模式。
访问者是一种比较复杂的行为型设计模式,它包含了访问者和被访问元素部分。在使用访问者模式的时候,被访问元素通常不是单独存在的,他们存储在一个集合中,这个集合被称为“对象结构”,访问者通过遍历对象结构实现对其中存储的元素的逐个遍历。
定义
表示一个作用于某对象结构中的各个元素的操作。访问者模式让用户可以在不改变个元素的类的前提下定义作用域这些元素的新操作。
结构
- Visitor(访问者):抽象访问者为对象结构中的每一个具体元素声明了一个访问操作,它接收具体被访问元素作为参数
- ConcreteVisitor(具体访问者):抽象访问者的子类,实现了对于对象结构中每种元素类型的具体操作
- Element(抽象元素):声明了可以接受抽象访问者的方法,同时是对象结构中的基本类型
- ConcreteElement(具体元素):实现了接收方法,代表了各个类型
- ObjectStructure(对象结构):存放元素对象,执行遍历方法
实例
// 针对事物的处理方式,售货员和顾客都是访问者,售货员访问食物后计算价格,顾客访问事食物后计算卡路里
package visitor;
public interface Food {
void accept(Person person);
}
package visitor;
// 变量类型从简
public class Cucumber implements Food{
private int weight;
private int unitPrice;
private int cal;
public Cucumber(int weight, int unitPrice, int cal) {
this.weight = weight;
this.unitPrice = unitPrice;
this.cal = cal;
}
public int getWeight() {
return weight;
}
public int getUnitPrice() {
return unitPrice;
}
public int getCal() {
return cal;
}
@Override
public void accept(Person person) {
person.caculate(this);
}
}
package visitor;
public class Bread implements Food{
private Integer num;
private Integer price;
private int cal;
public Bread(Integer num, Integer price, int cal) {
this.num = num;
this.price = price;
this.cal = cal;
}
public Integer getNum() {
return num;
}
public Integer getPrice() {
return price;
}
public int getCal() {
return cal;
}
@Override
public void accept(Person person) {
person.caculate(this);
}
}
package visitor;
public interface Person {
void caculate(Cucumber cucumber);
void caculate(Bread bread);
}
package visitor;
public class SalesMan implements Person{
@Override
public void caculate(Cucumber cucumber) {
System.out.println("黄瓜价格:" + cucumber.getUnitPrice() * cucumber.getWeight());
}
@Override
public void caculate(Bread bread) {
System.out.println("面包价格" + bread.getNum() * bread.getPrice());
}
}
package visitor;
public class Customer implements Person{
@Override
public void caculate(Cucumber cucumber) {
System.out.println("黄瓜热量:" + cucumber.getCal()*cucumber.getWeight());
}
@Override
public void caculate(Bread bread) {
System.out.println("面包热量:" + bread.getCal() * bread.getNum());
}
}
package visitor;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<Food> foods = new ArrayList<>();
foods.add(new Cucumber(1,10,100));
foods.add(new Bread(1,20,500));
Person p1 = new SalesMan();
foods.forEach(food -> food.accept(p1));
Person p2 = new Customer();
foods.forEach(food -> food.accept(p2));
}
}
优点
- 访问者模式中增加新的访问者特别方便,复合开闭原则
- 访问者将有访问功能的行为集中到一个对象上,职责明确,复合单一职责原则
缺点
- 增加或者修改被访问元素比较困难,需要同步修改访问者类,违背开闭原则
- 访问者访问的是具体的元素而非抽象接口,违背了迪米特法则
- 访问者模式破坏了对象的封装性,访问者必须知道被访问元素的内部结构
适用场景
- 一个对象结构包含了多种类型,希望对这些对象实施依赖其类型的操作
- 需要对一个对象结构中的对象进行很多不同并且不相关的操作,同时不希望增加新操作时修改这些类
- 对象结构中元素修改的可能性很小,操作修改的可能性很大