定义
封装作用与某种数据结构(如List/Set/Map等)中的各元素的操作。它可以使你在不改变各元素的类的前提下,定义作用于这些元素的操作。但需要这些被操纵的元素相对稳定。
简而言之,就是专门有个对象来访问某种数据结构的元素,且不改变这些元素。使元素本身与操作分离。
访问者模式能把处理方法从数据结构中分离出来,并可以根据需要增加新的处理方法,且不用修改原来的程序代码与数据结构,这提高了程序的扩展性和灵活性。
类型
行为型
适用场景
- 一个数据结构(如List/Set/Map等)包含很多类型对象
- 数据结构与数据操作分离
优点
- 扩展性好。增加新的操作很容易,即增加一个新的访问者。
- 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
- 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
- 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
缺点
- 增加新的数据结构困难
- 具体元素变更比较麻烦,即被访问对象中的元素改变,那么访问者中定义的操作也要随之改变,违背了开闭原则。
- 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。
UML类图
- Visitor:接口或者抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁地修改Visitor接口,如果这样则不适合使用访问者模式。
- ConcreteVisitorA、ConcreteVisitorB:具体的访问类,它需要给出对每一个元素类访问时的具体行为。
- Element:、接口或者抽象类,它定义了一个接受访问者的方法(Accept),其意义是指每一个元素都要可以被访问者访问。
- ConcreteElementA、ConcreteElementB:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
- ObjectStructure:定义当中所说的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问。
代码实例
以访问免费课程和付费课程为例,下面是UML图:
- Course(Element)
public abstract class Course {
protected String name;
public String getName() {
return name;
}
public Course(String name) {
this.name = name;
}
public abstract void accept(Visitor visitor);
}
- FreeCourse(ConcreteElement)
public class FreeCourse extends Course {
public FreeCourse(String name) {
super(name);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
- PaidCourse(ConcreteElement)
public class PaidCourse extends Course {
private String price;
public PaidCourse(String name,String price) {
super(name);
this.price = price;
}
public String getPrice() {
return price;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
- Visitor
public interface Visitor {
void visit(FreeCourse freeCourse);
void visit(PaidCourse paidCourse);
}
- CourseVisitor(ConcreteVisitor)
public class CourseVisitor implements Visitor {
@Override
public void visit(FreeCourse freeCourse) {
System.out.println(freeCourse.getName());
}
@Override
public void visit(PaidCourse paidCourse) {
System.out.println(paidCourse.getName() + ":" + paidCourse.getPrice());
}
}
- CourseStructure(ObjectStructure)
public class CourseStructure {
private List<Course> courses = new ArrayList<>();
public void add(Course course){
courses.add(course);
}
public void remove(Course course){
courses.remove(course);
}
public void accept(Visitor visitor){
for (Course course : courses){
course.accept(visitor);
}
}
}
访问者意在针对不同的数据类型对象作出不同的行为。
访问者模式与其他模式
访问者模式本身在开发中使用的较少。一般会和迭代器模式和组合模式联用。
- 迭代器模式。因为访问者模式中的ObjectStructure是一个包含元素角色的容器,当访问者遍历容器中的所有元素时,常常要用迭代器。
- 组合模式。因为访问者模式中的Element可能是叶子对象或者是容器对象,如果元素对象包含容器对象,就必须用到组合模式。