访客模型,重点都在访客这个词上。当我们想到采访时,我们一定会想到新闻采访,两个人面对面坐着。从字面理解:其实就相当于受访者(公众人物)把采访者(记者)当外人,不想让你随便动。你要什么,我搞定了就给你(调用你的方法)。01什么是访客模式?访问者模式的定义如下,就是在不事先改变数据结构的情况下定义新的操作。将作用于某个数据结构中每个元素的一些操作封装起来,它可以在不改变数据结构的情况下定义作用于这些元素的新操作。但是在实际应用中,我发现有些例子并不是这样的。有些例子不是稳定的数据结构,而是稳定的算法。在树一看来,访客模式就是:固定不变,打开变化。说一个生活中的例子:科学家接受记者采访。我们都知道,科学家接受采访,肯定有流程限制,不可能你随便问。我们假设流程是:先问科学家的求学经历,然后说你的工作经历,最后说你的科研成果。那么在这个过程中,什么是固定的呢?固定的是面试流程。发生了什么变化?不同的是,不同的记者可能会就学校经历提出不同的问题。按照我们之前的理解,访客模式其实就是固定不变的东西,开放变化的东西。那么对于被采访的科学家这件事,我们可以这样抽象出来。首先,我们需要有一个Visitor类,定义了外部(记者)可以做的一些事情(就学经历、工作经历、科研成果提问)。publicinterfaceVisitor{publicvoidaskSchoolExperience(Stringname);publicvoidaskWorkExperience(Stringname);publicvoidaskScienceAchievement(Stringname);}然后声明一个XinhuaVisitor类来实现Visitor类,意思是新华社的记者(访客)要拜访科学家。publicclassXinhuaVisitorimplementsVisitor{@OverridepublicvoidaskSchoolExperience(Stringname){System.out.printf("请问%s:在学校最大的成就是什么?\n",name);}@OverridepublicvoidaskWorkExperience(Stringname){System.out.printf("Excuse我%s:工作中最难忘的事情是什么?\n",name);}@OverridepublicvoidaskScienceAchievement(Stringname){System.out.printf("请问%s:最大的科学成就是什么?",name);}}然后声明一个Scientist类,表示是科学家。科学家通过accept()方法收到记者(访问者)的采访申请并存储。科学家定义了一种采访方法来固定采访过程。只有我教你问什么,我才会让你(记者)提问。publicclassScientist{privateVisitorvisitor;privateStringname;privateScientist(){}publicScientist(Stringname){this.name=name;}publicvoidaccept(Visitorvisitor){this.visitor=visitor;}publicvoidinterview(){System.out.println("------------访问开始------------");System.out.println("---开始谈学校经历---");visitor.askSchoolExperience(name);System.out.println("---开始谈工作经历---");visitor.askWorkExperience(name);System.out.println("---开始谈科研成果---");visitor.askScienceAchievement(name);}}最后声明一个场景类Client来模拟面试过程。publicclassClient{publicstaticvoidmain(String[]args){Scientistyang=newScientist("杨振宁");yang.accept(newXinhuaVisitor());yang.interview();}}运行结果为:------------面试开始----------------开始说学校经历---请问杨振宁:在学校最大的收获是什么?---开始谈工作经历---杨振宁:工作中最难忘的事情是什么?---开始说说科研成果---请问杨振宁:最大的科研成果是什么?看到这里,大家对visitor模型的本质有了更感性的认识(固定不变的,开放改变的)。在这个例子中,不变的是面试流程,变化的是可以问不同的问题。总的来说,访问者模式的类结构如下图所示:Visitor访问者接口。访问者界面定义了访问者可以做什么。这就需要你去分析哪些是变量,把这些变量的内容抽象成访问者接口的一个方法,并开放出来。访问者的信息实际上是通过访问者的参数传递的。ConcreteVisitor具体访问者。具体访问者定义了特定类型访问者的实现。对于新华社记者来说,他们更关心杨振宁的科研成果,所以在提问的时候,更倾向于挖掘成果。但对于青年报记者来说,他们的读者是青少年,更关心的是杨振宁在学习和工作中的精神状态。元素混凝土元素。这是指被访问的特定类,在我们的例子中是科学家类。一般情况下,我们会提供一个accept()方法来接收访问者参数,相当于接受它的示例应用。但是这个方法不是必须的,只要你能拿到visitor对象,你可以定义这个参数以任何方式传递。对于访问者模式,三个最重要的类是Visitor、ConcreteVisitor和Element。Visitor和ConcreteVisitor定义了访问者可以做的具体事情,将visitor的参数通过参数传递给访问者。Element通过各种方法获得访问者的对象,常用的是通过accept()方法,但这不是绝对的。需要注意的是,我们学习设计模式的重点是了解类之间的关系以及它们传递的信息。至于通过什么方式传递,是通过accept()方法还是通过构造函数,不是重点。02访客模式的实际应用前面我们用一个生活例子来帮助大家理解访客模式。相信大家对访客模式应该有一个感性的认识。为了回归到编程实践本身,让大家对访问者模式有一个更好的实践认识。下面我们将从软件编程的角度来谈谈访客模式在开源框架中的应用。文件树遍历JDK中有文件操作,我们自然清楚。有文件操作,自然会有文件夹遍历操作,即访问某个文件夹下的所有文件或文件夹。试想一下,如果我们要打印出某个文件夹下所有文件和文件夹的名称,需要怎么做呢?很简单,其实就是直接做一个树遍历,然后把名字打印出来!没错,这确实是正确答案!那如果我想统计所有文件和文件夹的个数呢?然后再遍历一遍,然后用计数器一直加一!是的,这也是正确答案!但是你有没有发现,在这两个过程中,我们有相同的操作:遍历文件树。无论是打印文件名还是计算文件树,我们都需要遍历文件树。而无论是哪个进程,我们最终想要的都是访问文件。还记得我们说过的设计模式的本质吗?设计模式的本质就是找出什么是不变的,然后找出什么是变化的,然后找到一个合适的数据结构(设计模式)来承载这种变化。在这个例子中,不变的是文件树的遍历,变化的是对文件的不同访问操作。显然,访客模式更适合承载这种变化。我们可以固定这个不变的东西(文件树的遍历),打开改变的东西(文件的具体操作)。JDK对文件树的遍历实际上是使用访问者模式实现的。JDK中声明了一个FileVisitor接口,定义了遍历可以做的操作。publicinterfaceFileVisitor
