概念
封装了一些作用于某种结构中的各个元素的操作(将数据结果与元素操作进行分离),可以在不改变这个数据结构的前提下,定义作用于这些元素的新的操作
结构
抽象访问者角色
定义了对每一个元素的访问行为,它的参数就是被访问的元素对象,它的方法个数理论上与元素个数保持一致(也看出访问者模式要求元素的个数不能改变)
具体访问者角色
完成抽象访问角色定义的对每一个元素的访问行为的具体实现
抽象元素角色
定义了一个接收访问者的接口方法,使元素可以被访问者访问
具体元素角色
完成抽象元素角色定义的接收访问者的接口方法的具体实现
对象结构角色
一个容器性质的类,会含有一组元素,并且可以遍历这些元素给访问者访问
示例
目标:有宠物这一种类(元素),定义其下有狸花猫、田园犬两个品种;有铲屎官(访问者),定义其下有主人、陌生人。铲屎官可以在家中投喂家中已有宠物
抽象元素角色
package 设计模式.行为型模式.访问者模式;
import lombok.Getter;
/**
* 宠物:抽象元素角色
*/
@Getter
public abstract class Pet {
/** 名称 */
protected String name;
/**
* 构造方法
*/
public Pet(String name) {
this.name = name;
}
/**
* 接受投喂
*/
abstract void accept(ShitShovelOfficer officer);
}
具体元素角色一
package 设计模式.行为型模式.访问者模式;
/**
* 狸花猫:具体元素角色
*/
public class TabbyCat extends Pet {
/**
* 构造方法
*/
public TabbyCat(String name) {
super(name);
}
/**
* 接受投喂
*/
@Override
public void accept(ShitShovelOfficer officer) {
// 调用铲屎官的投喂方法
officer.feeding(this);
System.out.println(this.getName() + ":感谢铲屎官" + officer.getName() + "的投喂,喵~");
}
}
具体元素角色二
package 设计模式.行为型模式.访问者模式;
/**
* 田园犬:具体元素角色
*/
public class PastoralDog extends Pet {
/**
* 构造方法
*/
public PastoralDog(String name) {
super(name);
}
/**
* 接受投喂
*/
@Override
public void accept(ShitShovelOfficer officer) {
// 调用铲屎官的投喂方法
officer.feeding(this);
System.out.println(this.getName() + ":感谢铲屎官" + officer.getName() + "的投喂,汪~");
}
}
抽象访问者
package 设计模式.行为型模式.访问者模式;
import lombok.Getter;
/**
* 铲屎官:抽象访问者角色
*/
@Getter
public abstract class ShitShovelOfficer {
/** 名称 */
protected String name;
/**
* 构造方法
*/
public ShitShovelOfficer(String name) {
this.name = name;
}
/**
* 喂食狸花猫
*/
abstract void feeding(TabbyCat cat);
/**
* 喂食田园犬
*/
abstract void feeding(PastoralDog dog);
}
具体访问者一
package 设计模式.行为型模式.访问者模式;
/**
* 主人:具体访问者角色
*/
public class TheHost extends ShitShovelOfficer {
/**
* 构造方法
*/
public TheHost(String name) {
super(name);
}
/**
* 喂食狸花猫
*/
@Override
public void feeding(TabbyCat cat) {
System.out.println(this.getName() + ":开始投喂狸花猫" + cat.getName());
}
/**
* 喂食田园犬
*/
@Override
public void feeding(PastoralDog dog) {
System.out.println(this.getName() + ":开始投喂田园犬" + dog.getName());
}
}
具体访问者二
package 设计模式.行为型模式.访问者模式;
/**
* 陌生人:具体访问者角色
*/
public class Someone extends ShitShovelOfficer {
/**
* 构造方法
*/
public Someone(String name) {
super(name);
}
/**
* 喂食狸花猫
*/
@Override
public void feeding(TabbyCat cat) {
System.out.println(this.getName() + ":开始投喂狸花猫" + cat.getName());
}
/**
* 喂食田园犬
*/
@Override
public void feeding(PastoralDog dog) {
System.out.println(this.getName() + ":开始投喂田园犬" + dog.getName());
}
}
对象结构角色
package 设计模式.行为型模式.访问者模式;
import java.util.ArrayList;
import java.util.List;
/**
* 家:对象结构角色
*/
public class Home {
/** 家中可有很多的宠物 */
private List<Pet> petList = new ArrayList<>();
/**
* 收养宠物
*/
public void adoption(Pet pet) {
petList.add(pet);
}
/**
* 开始投喂
*/
public void startFeed(ShitShovelOfficer officer) {
petList.forEach(pet -> {
pet.accept(officer);
});
}
}
测试类
package 设计模式.行为型模式.访问者模式;
/**
* 测试类
*/
public class Demo {
public static void main(String[] args) {
// 创建主人
ShitShovelOfficer theHost = new TheHost("主人");
// 创建陌生人
ShitShovelOfficer someone = new Someone("陌生人");
// 创建狸花猫
Pet tabbyCat = new TabbyCat("咪咪");
// 创建田园犬
Pet pastoralDog = new PastoralDog("小黑");
// 创建家
Home home = new Home();
// 家中收养宠物
home.adoption(tabbyCat);
home.adoption(pastoralDog);
// 主人喂养宠物
home.startFeed(theHost);
// 陌生人喂养宠物
home.startFeed(someone);
}
}
优缺点
优点
1.可以在不修改对象结构中元素的前提下,为元素添加新的功能
2.通过访问者来定义整个对象结构通用的功能,提高了代码的复用性
3.将相关的行为封装到一起构成一个访问者类,使每一个访问者类的功能单一好维护
缺点
1.每增加一个新的元素类,需要在每一个访问者角色中添加对应的操作,违背了“开闭原则”
2.访问者模式依赖具体元素角色,而不是依赖抽象元素角色
应用场景
1.对象结构相对稳定,但操作算法经常变化时,可以使用访问者模式
2.对象结构中的元素需要提供多种不同且不相关的操作、且需要避免让这些操作的变化影响对象的结构时,可以使用访问者模式