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() )
cat.accept( Guest() )
}
sealed class LivingCreature {
abstract fun accept(visitor: Visitor)
}
interface Visitor {
fun visit(creature : Human)
fun visit(creature : Cat)
}
class Human(val name: String): LivingCreature() {
override fun accept(visitor: Visitor) = visitor.visit(this)
}
class Cat(val colour: String): LivingCreature(){
override fun accept(visitor: Visitor) = visitor.visit(this)
}
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.