访问模式是一种行为型模式,访问者模式的定义:“表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作”。
前面我们学习了组合模式,组合模式可以让我们快速构建树,对于数据结构来说,树的怎么定义实际上就是一种数据结构,而对于树的遍历操作等可以交给访问者来做,这样的好处是,首先进一步做了分离,操作和具体实现的分离,同时按照访问者的定义来说即使将来需要新增对元素的操作,也可以不改变各个元素的类。
Visitor是抽象的访问者,定义谁可以被访问者,图中的ConcreteElementA和ConcreteElementB的两个参数就是具体可以被访问的实体。
当然对于访问者来说不可能访问了啥事不干吧,你要去拜访一个人也得因为什么事情要去,即使只是去蹭个饭,聊个天。所以ConcreteVisitor是访问者具体要做的事。
Element则是用于接受访问者,抽象的被访问者,相当于就是一个主人,你要去访问一个人,而这个人的抽象就是Element。
ConcreteElementA和ConcreteElementB都是被访问者的具体实现。也就是具体的被访问的人。
ObjectStructure是对象结构,我们说访问者是在不改变原元素的情况下增加新的功能,而这个ObjectStructure你可以理解为就是我们的数据结构,例如构成树的类,或组合模式中的部门和人员,文件或文件夹等。

Visitor是抽象的访问者,只是简单的定义了要访问谁,可以看到可以访问的是部门类和人员类(之前组合模式中的类),而具体的要怎么访问,访问了要干什么?哪怕是去蹭饭,去聊天,都应该由子类决定。
public abstract class Visitor {public abstract void visit(Department department);public abstract void visit(Person person);}
ListVisitor是具体的访问者,具体访问了要干什么都是他来做,而这儿只是对如果是部门的话就输出部门名以及部门下的所有人,而如果只是人的话则打印人的名称。
public class ListVisitor extends Visitor{private String currentDir = "";@Overridepublic void visit(Department department) {String name = currentDir + "/" + department;System.out.println(name);currentDir = name;List<OrganizationalStructure> son = department.son;for (OrganizationalStructure shuGuo : son) {shuGuo.accept(this);}}@Overridepublic void visit(Person person) {System.out.println(currentDir+"/"+person);}}
Element用于接受访问者,相当于自己就是被访问的,如果有人来访问你,你肯定要去迎接吧,所有accpent就是用于迎接或接受访问者。
public interface Element {/*** 用于接受访问者* @param visitor*/void accept(Visitor visitor);}
OrganizationalStructure是之前在组合模式中用到部门和人员的抽象,就是组织结构。只是我们删除了一些对组织结构的遍历的抽象方法,同时实现了Elenent,毕竟OrganizationalStructure就是被访问者,现在有人要访问你,肯定要迎接,那就得实现Element。
public abstract class OrganizationalStructure implements Element {/*** 获取名称* @return*/public abstract String getName();@Overridepublic String toString() {return getName();}}
Department部门类,继承于组织结构,同时现在也是具体的被访问者,毕竟父类实现了Element,实际上Department可以去实现Element类,但是为了让Department和Person都实现于Element,所以采用的是OrganizationalStructure实现于Element。
public class Department extends OrganizationalStructure {/*** 部门名*/private String name;/*** 部门中的人员或下级部门*/public List<OrganizationalStructure> son = new ArrayList();public Department(String name) {this.name = name;}/*** 获取部门名称* @return*/@Overridepublic String getName() {return name;}/*** 当前部门添加子部门或人员信息* @param shuGuo* @return*/public OrganizationalStructure add(OrganizationalStructure shuGuo){son.add(shuGuo);return this;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}}
同理对于Person来说也是组织结构的一部分,所以也继承于OrganizationalStructure,当然也实现了accept方法,而对于accept方法来说,accept方法使用visitor.visit(this)相当于将访问者要访问的人设置了自己。
public class Person extends OrganizationalStructure {/*** 人员名称*/private String name;public Person(String name) {this.name = name;}/*** 获取人员名* @return*/@Overridepublic String getName() {return name;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}}
而现在构建组织结构后,不再像组合模式中调用printList方法进行结构的打印,而是使用访问者模式,相当于将结构和功能做了一个拆分;如果后续需要对组织结构增加新的打印方式,此时对于部门和人员类来说是不需要发生变化的,这就是访问模式的优势。
public class Test {public static void main(String[] args) {Department department1 = new Department("谋士部");Department department2 = new Department("武将部");department1.add(new Person("诸葛亮"));department1.add(new Person("庞统"));department1.accept(new ListVisitor());System.out.println();department2.add(new Person("张飞"));department2.add(new Person("赵云"));department2.add(new Person("黄忠"));department2.add(new Person("马超"));department2.add(new Person("关羽"));Department department3 = new Department("弓箭部");department3.add(new Person("小兵1"));department3.add(new Person("小兵2"));department2.add(department3);department2.accept(new ListVisitor());}}
/谋士部/谋士部/诸葛亮/谋士部/庞统/武将部/武将部/张飞/武将部/赵云/武将部/黄忠/武将部/马超/武将部/关羽/武将部/弓箭部/武将部/弓箭部/小兵1/武将部/弓箭部/小兵2
访问者模式中的角色
抽象访问者(Visitor):抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是 visit 方法的参数定义哪些对象是可以被访问的; 文中相当于Visitor。
具体访问者(ConcreteVisitor):访问者访问到一个类后该怎么干(哎,这个别读歪了),要做什么事情;文中相当于ListVisitor。
抽象元素(Element):接口或者抽象类,声明接受那一类型的访问者访问,程序上是通过 accept 方法 中的参数来定义;文中相当于Element类。
具体元素:(ConcreteElement):实现 accept 方法,通常是 visitor.visit(this),文中相当于部门以及人员类。
结构对象(ObjectStruture):容纳多个不同类、不同接口的容器,比如 List、Set、Map 等,在项目中, 一般很少抽象出来这个角色;
参考文献《图解设计模式》
代码获取地址:https://gitee.com/bughong/design-pattern




