访问者模式
- 定义:封装作用于某数据结构(如List/Set/Map等)中的各元素操作
- 补充定义:可以在不改变各元素类的前提下,定义作用于这些元素的操作
- 类型:行为型
- 适用场景:
- 一个数据结构(如List/Set/Map等)包含很多类型对象
- 数据结构与数据操作分离
- 优点:增加新的操作很容易,即增加一个新的访问者
- 缺点:
- 增加新的数据结构很困难
- 具体元素变更比较麻烦
- 相关设计模式:
- 访问者模式和迭代器模式:都是在某种数据结构上处理,访问者模式主要用于对保存在数据结构中的元素进行某种特定的处理,迭代器模式主要用于逐个遍历保存在数据结构中的元素,重点在于遍历。
Coding
public abstract class Course { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public abstract void accept(IVisitor visitor); } public class FreeCourse extends Course { @Override public void accept(IVisitor visitor) { visitor.visit(this); } } public class CodingCourse extends Course { private int price; public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } @Override public void accept(IVisitor visitor) { visitor.visit(this); } } public interface IVisitor { void visit(FreeCourse freeCourse); void visit(CodingCourse codingCourse); } public class Visitor implements IVisitor { /** * 访问免费课程,打印所有免费课程名称 * * @param freeCourse */ @Override public void visit(FreeCourse freeCourse) { System.out.println("免费课程:" + freeCourse.getName()); } /** * 访问实战课程,打印所有实战课程名称及价格 * * @param codingCourse */ @Override public void visit(CodingCourse codingCourse) { System.out.println("实战课程:" + codingCourse.getName() + " 价格:" + codingCourse.getPrice() + "元"); } }
UML
测试
public class Test { public static void main(String[] args) { List<Course> courseList = new ArrayList<Course>(); FreeCourse freeCourse = new FreeCourse(); freeCourse.setName("SpringMVC数据绑定"); CodingCourse codingCourse = new CodingCourse(); codingCourse.setName("Java设计模式"); codingCourse.setPrice(299); courseList.add(freeCourse); courseList.add(codingCourse); for (Course course : courseList) { course.accept(new Visitor()); } } } ============输出============= 免费课程:SpringMVC数据绑定 实战课程:Java设计模式 价格:299元
- 说明:这个模式也很简单,定义了一个免费课程类和实战课程类,都继承了课程抽象类,访问者接口中有两个visit方法,分别用于不同课程的访问,其实现类中就是对应课程的访问。它的核心是根据不同的visitor对相同的数据(都是Course)产生不同的操作行为
源码中的应用
- 在日常中并不常用,如果遇到数据加载和数据分离的情况下,就可以考虑是否使用访问者模式
- java.nio.file.FileVisitor:文件访问者,通过它去遍历文件树会比较方便,比如查找文件夹内符合一定条件的文件,或者按时间查看某一天创建的文件,它的实现就一个SimpleFileVisitor,如果里面的方法不能满足我们的要求的话,可以继承这个类,覆盖里面的方法,或者自己写一个FileVisitor的实现。
public interface FileVisitor<T> { FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) throws IOException; FileVisitResult visitFile(T file, BasicFileAttributes attrs) throws IOException; FileVisitResult visitFileFailed(T file, IOException exc) throws IOException; FileVisitResult postVisitDirectory(T dir, IOException exc) throws IOException; }
- org.springframework.beans.factory.config.BeanDefinitionVisitor:Bean定义的访问者,定义Bean的各种属性,然后用里面的properties进行填充,这个类主要对Bean的各种数据进行分析把它们进行分解,比如下面这个visitBeanDefinition方法
public class BeanDefinitionVisitor { private StringValueResolver valueResolver; public BeanDefinitionVisitor(StringValueResolver valueResolver) { Assert.notNull(valueResolver, "StringValueResolver must not be null"); this.valueResolver = valueResolver; } public void visitBeanDefinition(BeanDefinition beanDefinition) { this.visitParentName(beanDefinition); this.visitBeanClassName(beanDefinition); this.visitFactoryBeanName(beanDefinition); this.visitFactoryMethodName(beanDefinition); this.visitScope(beanDefinition); this.visitPropertyValues(beanDefinition.getPropertyValues()); ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues(); this.visitIndexedArgumentValues(cas.getIndexedArgumentValues()); this.visitGenericArgumentValues(cas.getGenericArgumentValues()); } }