The Visitor template is deprecated for Kotlin, but it's worth knowing

Consider the Visitor design pattern and show that you should not use it when programming on Kotlin. There will be a theory, a minimalistic implementation, the implementation of a replacement and the arguments for replacement, supported by practical research. There will be no class diagrams. You can try everything online at play.kotlinlang.org



On my extreme , JSON . Finite State Machine, Visitor. State Machine , Visitor, β€” "!". , , . β€” , , . β€” , . β€” .



Visitor. . , LinkedIn , , . Visitor , ( ). Hollywood Agent β€” " β€” ". - . , . Python JavaScript . "", β€” β€” , . Java .


, , ( ) . , , ( ), . β€” . Kotlin " " , ( , ).


β€” Visitor . Visitor , , . , , , , . , , , , . Visitor.



, : . sealed class , .
: .


fun main() {
    Guest().visit( Human(""))
    Guest().visit( Cat(""))
}

sealed class LivingCreature
class Human(val name: String): LivingCreature()
class Cat(val colour: String): LivingCreature()

interface Visitor {
    fun visit(creature : Human)
    fun visit(creature : Cat)
}
class Guest: Visitor {
    override fun visit(human: Human) = println("  ${human.name}")
    override fun visit(pet: Cat) = println("  (  ${pet.colour})")
}

β€” . , visit . , Human Cat LivingCreature. :


fun main() {
    val human: LivingCreature = Human("")
    val cat  : LivingCreature = Cat("")
    Guest().visit( human) 
    Guest().visit( cat )
}

sealed class LivingCreature
class Human(val name: String): LivingCreature()
class Cat(val colour: String): LivingCreature()

interface Visitor {
    fun visit(creature : Human)
    fun visit(creature : Cat)
}
class Guest: Visitor {
    override fun visit(human: Human) = println("  ${human.name}")
    override fun visit(pet: Cat) = println("  (  ${pet.colour})")
}

, visit(LivingCreature). , , β€” , , . , , .


Visitor. , , β€” Human Cat, , Guest (Visitor) . LivingCreature Visitor visit, .


fun main() {
    val human: LivingCreature = Human("")
    val cat  : LivingCreature = Cat("")
    human.accept( Guest() ) //   LivingCreature.accept( Visitor ),         
    cat.accept( Guest() ) // 
}

sealed class LivingCreature {
    abstract fun accept(visitor: Visitor) //    inline fun -     
}
interface Visitor {
    fun visit(creature : Human)
    fun visit(creature : Cat)
}
class Human(val name: String): LivingCreature() {
    override fun accept(visitor: Visitor) = visitor.visit(this) //    Human,    visit(Human)  -
}
class Cat(val colour: String): LivingCreature(){
    override fun accept(visitor: Visitor) = visitor.visit(this) //    Cat,    visit(Cat)  -
}

class Guest : Visitor{
    override fun visit(creature : Human) = println("  ${creature.name}")
    override fun visit(creature : Cat) = println("  (  ${creature.colour})")
}

β€” inline fun , - .
β€” visit , β€” ( ) . accept, β€” . Visitor.


β€” . . β€” accept. , . , β€” . Visitor.


, Visitor β€” , - .


" "?


LivingCreature Visitor.visit(LivingCreature)?


fun main() {
    val human: LivingCreature = Human("")
    val cat  : LivingCreature = Cat("")
    Guest().visit(human )
    Guest().visit( cat )
}

sealed class LivingCreature 
interface Visitor {
    fun visit(creature: LivingCreature)
}
class Human(val name: String): LivingCreature()
class Cat(val colour: String): LivingCreature()

class Guest : Visitor{
    override fun visit(creature : LivingCreature) = when(creature) {
        is Human -> println( "  ${creature.name}")
        is Cat -> println( "  (  ${creature.colour} ) ")
    }
}

, , . . ? , Java instanceof , , , JVM β€” Java. - β€” ++. "" . - , instanceof , JVM. , JVM .


It turns out that you can write Kotlin and Java performance tests in JHM , which I did. The result surprised me - the version with Visitor works many times slower than the version with when / smart cast . You can see it here:
my repository on GitHub


findings


  • Visitor is a great exercise for the mind and I would highly recommend trying it in unexpected situations, but out of academic interest.
  • Visitor improves visual Java code and allows you to express the behavior more clearly. A good option for generators, event handler patterns (see ANTLR).
  • Visitor loses in readability and performance to the β€œnaive” solution on Kotlin due to the peculiarities of the language.

All Articles