是一种将数据结构与数据操作分离的设计模式。是指封装一些作用于某些数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。属于行为型模式。
访问者模式的基本思想是针对系统中拥有固定类型的对象结构(元素),在其内提供一个accept()方法用来接受访问者的访问,不同的访问者对同一元素的访问内容不同,使得相同的元素集合可以产生不同功能的数据结果。accept()方法可以接收不同的访问者对象,然后在内部将自己(元素)转发到接收到的访问者对象的visit()方法内。访问者内部对应类型的visit()方法就会得到回调执行,对元素进行操作。也就是通过两次动态分发(第一次是对访问者的分发accept()方法,第二次是对元素的分发visit()方法)才最终将一个具体的元素传递到一个具体的访问者。如此就解耦了数据结构与操作,且数据操作不会改变元素状态。
访问者的核心是解耦数据结构与数据操作,使得对元素的操作具备优秀的扩展性。可以通过扩展不同的数据类型(访问者)实现对相同元素集的不同的操作。
当系统中存在类型数目稳定的一类数据结构时,可以通过访问者模式方便地实现对该类型所有数据类型的不同操作,而不会对数据产生任何副作用。简而言之,就是对集合中不同类型的数据进行操作,则使用访问者模式
工作绩效:员工分为工程师和经理;管理层友CEO和CTO。CTO关注工程师的代码量,经理的新产品数量;CEO关注的是工程师的KPI和经理的KPI和新产品数量。
public interface IVisitor {
void visit(Engineer engineer);
void visit(Manager manager);
}
public class CEOVisitor implements IVisitor {
public void visit(Engineer engineer) {
System.out.println(String.format("工程师 %s ,KPI %s", engineer.name, engineer.kpi));
}
public void visit(Manager manager) {
System.out.println(String.format("经理 %s ,KPI %s , 新产品数量 %s", manager.name, manager.kpi, manager.getProducts()));
}
}
public void visit(Engineer engineer) {
System.out.println(String.format("工程师: %s ,代码行数: %s", engineer.name, engineer.getCodeLines()));
}
public void visit(Manager manager) {
System.out.println(String.format("经理: %s ,新产品数量: %s", manager.name, manager.getProducts()));
}
public abstract class Employee {
public String name;
public int kpi;
public Employee(String name) {
this.name = name;
}
public abstract void accept(IVisitor visitor);
}
public class Engineer extends Employee{
public Engineer(String name) {
super(name);
}
public void accept(IVisitor visitor) {
visitor.visit(this);
}
public int getCodeLines(){
return new Random().nextInt(100000);
}
}
public class Manager extends Employee{
public Manager(String name) {
super(name);
}
public void accept(IVisitor visitor) {
visitor.visit(this);
}
public int getProducts(){
return new Random().nextInt(10);
}
}
public class BusinessReport {
private List<Employee> employees = new LinkedList<Employee>();
public BusinessReport() {
employees.add(new Manager("manager_A"));
employees.add(new Manager("manager_B"));
employees.add(new Engineer("engineer_A"));
employees.add(new Engineer("engineer_B"));
employees.add(new Engineer("engineer_C"));
}
public void showReport(IVisitor visitor) {
for (Employee employee : employees) {
employee.accept(visitor);
}
}
}
public class Test {
public static void main(String[] args) {
BusinessReport report = new BusinessReport();
report.showReport(new CEOVisitor());
report.showReport(new CTOVisitor());
}
}
访问者模式的最大优点就是增加访问者非常容易。
变量被声明时的类型叫做变量的静态类型(Static Type),有些人又把静态类型叫做明显类型(Apparent Type);而变量所引用的对象的真实类型又叫做变量的实际类型(Actual Type)例如:
List list = null;
list = new ArrayList();
声明了一个变量list,它的静态类型是List,而它的实际类型是ArrayList。根据对象的类型而对方法进行的选择,就是分派。分派又分为两种,即静态分派和动态分派。
静态分派就是按照便令的静态类型进行分派,从而确定方法的执行版本,静态分派在编译十七就尅确定方法的版本。而静态分派的典型应用就是方法重载。
public class Main {
public void test(String str){
System.out.println("str");
}
public void test(Integer integer){
System.out.println("integer");
}
public static void main(String[] args) {
String str = "1";
Integer integer = 1;
Main main = new Main();
main.test(str);
main.test(integer);
}
}
在静态分派判断的时候,我们根据多个判断依据(参数类型和个数)判断出了方法的版本,这个就是多分派的概念。Java是静态多分派语言。
对于动态分派,与静态分派相反,它不是在编译期确定的方法版本,而是在运行时才能确定的。而动态分派典型的应用就是多态的特性。
public interface Person {
void test();
}
class man implements Person{
public void test() {
System.out.println("man");
}
}
class woman implements Person{
public void test() {
System.out.println("woman");
}
}
class main {
public static void main(String[] args) {
Person man =new man();
Person woman = new woman();
man.test();
woman.test();
}
}
这段程序输出结果为man和woman,然而这里的test()方法版本无法根据Man和Woman的静态类型去半段,他们的静态类型都是Person接口。显然产生的输出结果就是因为test()方法的版本实在运行时判断的这就是动态分派。
动态分派判断的方法实在运行时获取Man和Woman的实际引用类型,在确定方法的版本,而由于判读的依据只是实际引用类型,只有一个判断依据,所以这就是单分派的概念。Java是动态单分派语言。
动态双分派就是在运行时以及两个实际类型去判断一个方法的运行行为,访问者模式实现的手段是进行了两次动态单分派来达到这个效果。
JDK的NIO模块下的FileVisitor
Spring IOC 中的BeanDefinitionVisitor类