2015-11-09 2 views
1

Есть ли какие-либо трюки или общие подходы к реализации visitor pattern в Котлине? Все, что может быть неочевидным для новичков, но приводит к более сжатому или организованному коду.Лучший способ внедрения шаблона посетителя в Котлин

EDIT для уточнения: у меня есть AST со многими (~ 30) типами узлов в нем. В настоящее время каждый класс реализует свой собственный метод print(), который я хочу разделить на отдельный класс принтера. При использовании шаблона посетителя будет проще добавлять другие классы обхода AST, из которых их будет несколько.

+0

Вы просите кого-то, чтобы написать шаблон посетителя образец для Котлин от вашего имени? Вопрос широкий, открытый и, вероятно, должен быть помечен для закрытия. –

+0

Нет, я просто спрашиваю, как идиоматически реализовать конкретный образец на языке, который я все еще изучаю - вот и все. – Max

+0

Опубликуйте свое лучшее предположение, и дайте нам помочь вам настроить его. Вероятно, вы просто передадите лямбду узлам, которые пройдут и вызовут лямбду с посещенным узлом. Начните там, отправьте код, затем мы сможем помочь оттуда. –

ответ

4

Read this answer для Java 8, все это говорит также относится к Котлину:

Дополнение, сделанное на языке Java не оказывает каждое старое понятие устаревшим. Фактически, шаблон посетителя очень хорош в поддержке добавления новых операций.

Это относится к Kotlin. Подобно Java 8, он имеет Lambdas, SAM conversions и interfaces that allow default implementations.

Одно изменение, если вы делаете экземпляр класса проверки типа, вместо того чтобы использовать большое if заявление для каждого instanceof проверки, используйте when expression в Котлин:

На той же странице StackOverflow в другом ответе он говорит о том, Lambdas используется и показывает if заявление в Java, определяющее, какую лямбду позвонить. Вместо того, чтобы их Java sample:

if (animal instanceof Cat) { 
    catAction.accept((Cat) animal); 
} else if (animal instanceof Dog) { 
    dogAction.accept((Dog) animal); 
} else if (animal instanceof Fish) { 
    fishAction.accept((Fish) animal); 
} else if (animal instanceof Bird) { 
    birdAction.accept((Bird) animal); 
} else { 
    throw new AssertionError(animal.getClass()); 
} 

Используйте этот Котлин:

when (animal) { 
    is Cat -> catAction.accept(animal) 
    is Dog -> dogAction.accept(animal) 
    is Fish -> fishAction.accept(animal) 
    is Bird -> birdAction.accept(animal) 
    else -> throw AssertionError(animal.javaClass) 
} 

В Котлин вам не нужно бросать так как smart cast автоматически сделан, когда компилятор видит is проверку типа экземпляра.

Также в Котлин вы можете использовать Sealed Classes представлять ваши возможные варианты в иерархии, а затем компилятор может определить, если вы исчерпали все случаи означает, что вы не нуждаетесь в else в when заявлении.

В противном случае то, что верно на этой странице, и другие распространенные ответы на один и тот же вопрос - хорошая информация для Kotlin. Я не думаю, что так же часто можно увидеть фактический образец буквенного посетителя в Java 8, Scala или Kotlin, но, скорее, некоторые вариации, использующие lambdas и/или сопоставление шаблонов.

Прочие связанные статьи:

+0

Использование больших аргументов if-else/when с проверками типов - это то, что шаблон посетителя пытается избежать, не так ли? Следовательно, только решение во второй из связанных статей ([phd-in] (http://amrphd.blogspot.com.uy/2014/12/visitor-design-pattern-vs-lambda.html)) кажется действительный, так как там используется единственная отправка. Но использование метода accept с n lambdas вместо одного объекта посетителя также не является разумным. Проблема становится еще более очевидной, если у вас нет доступа к классам, которые будут дважды отправлены. Затем вы используете методы расширения, которые статически отправляются, либо ... – Juangamnik

0

Комбинация объектов компаньонов и лямбды могут быть использованы для достижения динамического посещения, например, так:

interface Visitable { fun visit()} 

class FooOne(): Visitable { 
    val title1 = "111" 
    companion object { var visit: (FooOne)->Unit = {} } 
    override fun visit() { FooOne.visit(this) } 
} 

class FooTwo(): Visitable { 
    val title2 = "222" 
    companion object { var visit: (FooTwo)->Unit = {} } 
    override fun visit() { FooTwo.visit(this) } 
} 

/* assign visitor functionality based on types */ 
fun visitorStars() { 
    FooOne.visit = {println("In FooOne: ***${it.title1}***") } 
    FooTwo.visit = {println("In FooTwo: ***${it.title2}***") } 
} 

/* assign different visitor functionality */ 
fun visitorHashes() { 
    FooOne.visit = { println("In FooOne: ###${it.title1}###") } 
    FooTwo.visit = {println("In FooTwo: ###${it.title2}###") } 
} 

fun main(args: Array<String>) { 
    val foos = listOf<Visitable>(FooOne(), FooTwo()) 
    visitorStars() 
    foos.forEach {it.visit()} 
    visitorHashes() 
    foos.forEach {it.visit()} 
} 

>>> 
In FooOne: ***111*** 
In FooTwo: ***222*** 
In FooOne: ###111### 
In FooTwo: ###222### 
Смежные вопросы