单派
在OOP
的编程语言中,比如java
或者kotlin
,都支持单派,即根据引用的实际类型去执行对应的方法,这也是多态的基础
例如:
interface Element{
fun foo()
}
class ElementA:Element{
override fun foo() {
println("this is element A")
}
}
class ElementB:Element{
override fun foo() {
println("this is element B")
}
}
接口Element
有两个子类,当我们面向接口编程时,总能执行实际类型对应的方法:
fun main(vararg args:String){
var ele:Element = ElementA()
ele.foo() ==>this is element A
ele = ElementB()
ele.foo() ==>this is element B
}
然而,当类型出现在参数列表时,比如:
fun invokeFoo(ele:ElementA){
ele.foo()
}
fun main(vararg args:String){
var ele:Element= ElementA()
invokeFoo(base) //无法通过编译
}
我们会发现,上面的语句无法通过编译,因为java不支持根据参数的实际类型进行派遣,在编译时,无法通过类型的静态检查
修改上面的两个子类,分别为他们添加方法:
class ElementA:Element{
override fun foo() {
println("this is element A")
}
fun methodA()=Unit
}
class ElementB:Element{
override fun foo() {
println("this is element B")
}
fun methodB()=Unit
}
现在,我们需要实现一个方法,接收一个Element
引用,并根据它的实际类型去分别调用methodA
或者methodB
,我们很容易就写出:
fun invokeFoo(ele:Element){
when (ele){
is ElementA -> ele.methodA()
is ElementB -> ele.methodB()
else -> throw IllegalArgumentException("ele 类型参数不匹配")
}
}
我们去判断ele
的实际类型,然后分别为不同类型执行不同的处理逻辑,比如为ElementA
调用methodA
然而,当Element
新增了子类,我们都要来修改这个方法,这个方法将会变得越来越臃肿。
为了能够简化这个方法,我们可能会想为每个子类都声明一个函数,从而构成一组重载函数,比如:
fun invokeFoo(ele:ElementA){
ele.methodA()
}
fun invokeFoo(ele:ElementB){
ele.methodB()
}
这样,每个Element
都有自己的一个处理函数,每次Element
添加了子类型,只要新增加一个对应的函数就可以了,而且每次执行而不需要额外的判断逻辑
但是根据上面的分析,java
并不支持根据参数的实际类型进行派遣。
使用Visitor
Visitor
模式使用的是双重派遣,基于上面的问题,我们引入新的类结构:
我们引入了新的类结构
Visitor
,在Visitor
中,我们为每个Element
的子类都声明了一个visit
函数,从而构成了一系列的重载函数。对于每个
Element
的子类中,我们都需要显示的重写accept
函数:
override fun accept(v: Visitor) {
v.visit(this)
}
在一个类中使用this
引用,静态编译时,编译器就会认为该引用的类型为当前方法所在类
当我们通过ele.accept(visitor)
来执行ele
的某个方法时,首先会根据ele
的实际类型进行派遣,进入accept
方法内之后,会根据visitor
的实际类型和this
的类型再次进行派遣,决定执行visitor
的哪个重载函数。
模拟代码:
interface Element {
fun accept(v: Visitor)
}
class ElementA : Element {
fun methodA() = println("visit element a")
override fun accept(v: Visitor) {
v.visit(this)
}
}
class ElementB : Element {
fun methodB() = println("visit element b")
override fun accept(v: Visitor) {
v.visit(this)
}
}
interface Visitor {
fun visit(ele: ElementA)
fun visit(ele: ElementB)
}
class VisitorA : Visitor {
override fun visit(ele: ElementA) {
ele.methodA()
}
override fun visit(ele: ElementB) {
ele.methodB()
}
}
fun main(vararg args: String) {
var ele: Element = ElementA()
ele.accept(VisitorA())
}
通过使用visitor
模式,当我们新增一个Element
的子类时,我们只需要在Visitor
的子类中为他新增一个visit
方法,而无需更改其他函数的逻辑,而且每个visit
方法都只关注于具体的处理逻辑。使用Visitor
模式更利于后期的维护。
网友评论