当前位置: 首页 > 科技观察

Java的双分配和Visitor模式

时间:2023-03-15 22:56:33 科技观察

DoubleDispatch(双分派)什么是双分派?在谈到面向对象编程时,经常会提到面向对象的“多态性”。关于多态,经常有一种说法是“父类引用指向子类对象”。这个父类引用指向子类对象的写法类似如下:Animalanimal=newDog();animal.bark();另一种常见形式是publicclassKeeper{publicvoidsay(Animala){System.out.println("Animalsay");}publicvoidsay(Dogdog){System.out.println("dogsay");}}Animalanimal=newAnimal();Animaldog=newDog();Keeperkeeper=newKeeper();keeper.say(animal);保持。说(狗);当上面的keeper调用say两次时,它会输出什么?它会调用两种不同的方法吗?其实在这两个调用中,都会这样调用say(Animala)。由于这些内容可以在编译时确定,所以这是Java的静态分布。从上图可以看出,两次调用生成的字节码确实指向了say(Animala)方法,并且在运行时直接执行了该方法,并输出了相应的内容。为什么对应的animal.bark()最终调用的是dog类的方法呢?这是判断具体方法接收者的类型,在运行时执行。这就是所谓的动态分配,在运行时确定具体的方法,实现面向对象的多态性。分发(Dispatch)分发指的是最终确定一个要执行的方法的过程。对于Java等静态语言,方法执行是通过SingleDispatch进行的。比如这样一行代码dog.eat(newBone())最后执行的是要选择的eat方法,只是根据狗的具体类型选择相应的方法,传入的参数不能影响选择的相应的方法。这是SingleDispatch为了让真正的参数传入,这里是Bone要真正发挥作用,需要用DoubleDispatch还是MultipleDispatch,也就是说最后决定调用哪个方法,不是不仅是方法的接收者,还取决于参数类型。Visitor模式在GoF的设计模式中,Visitor模式使用DoubleDispatch来达到调用真实对象的目的。对于访问者模式,最常见的例子是树遍历。例如,节点和叶子的处理方式存在差异。这是由于访问者的双重分布为不同的元素实现不同的内容。代码类似于:node.accept(newConcreateVisitor());leaf.accept(newConcreateVisitor());node中的accept方法会再次将其真实类型传回给visitorpublicvoidaccept(Visitorv){v.visit(this);}这时候在visitor中,可以根据真实类型调用具体的方法。对应node和leaf,有这样的方法:publicvoidvisit(Noden);publicvoidvisit(Leafl);Visitor一般包含visitor接口,在visitor接口中包含了每个要访问的Element对象的处理逻辑。在每个Element的具体实现中,将自己的类型传回给访问者进行二次分发,实现确切逻辑的调用。Tomcat中的ApplicationVisitor也在Tomcat中使用,一般是解析EL表达式。例如,org.apache.el.parser.Node类包含一个accept(NodeVisitorvisitor)方法。实际的Node类型有很多,但是在真正调用的时候,会通过publicvoidaccept(NodeVisitorvisitor)throwsException{visitor.visit(this);将真实类型传回给访问者,具体的方法会在访问者中调用,这样参数也能起到决定性的作用。publicvoidvisit(Nodenode)throwsELException{if(nodeinstanceofAstFunction){AstFunctionfuncNode=(AstFunction)node;Methodm=null;}elseif(xxx){}这里一般声明了多个访问方法,然后上面的visit(this)会直接定位到目标方法。以上就是Java中的各种分发,访问者模式以模式的形式实现了双重分发的效果。【本文为专栏作家“侯书城”原创稿件,转载请通过作者微信公众号“Tomcat物语”获得授权】点此查看本作者更多好文