Улучшение синтаксиса DSL - PullRequest
0 голосов
/ 20 октября 2018

Чтобы начать изучать дизайн DSL с использованием возможностей языка Kotlin, ниже я попробую игрушечный DSL для создания групп членов с именами участников.Я ищу указатели / подсказки для следующих

  1. Как мне избежать необходимости разделять группы точкой с запятой, если точка с запятой компилятор не дает

Группы.kt: 31: 45: ошибка: неразрешенная ссылка: member val grp = group {member {name ("Bob")} member {name ("Sandy")}}

Могу ли я использовать лямбду для установки name вместо вызова функции?

Могу ли я избежать, чтобы name был изменяемым в классе MEMBER?

Мой код

fun group(create: GROUP.() -> Unit) = GROUP().apply(create)

class GROUP {
    private val members = mutableSetOf<MEMBER>()

    fun member(create: MEMBER.() -> Unit) {
        val member = MEMBER()
        member.create()
        members.add(member)
    }

    override fun toString() = members.toString()

}

class MEMBER() {
    var name = ""
    set(value) {
        field = value
    }

    fun name(nameToSet: String) {
        name = nameToSet
    }
    override fun toString() = "MEMBER(" + name + ")"
}

fun main(args: Array<String>) {
    val grp = group { member { name ("Bob") }; member { name ("Sandy") } }
    println(grp)
}

В настоящее время код выше

[MEMBER (Боб), MEMBER (Песчаный)]

1 Ответ

0 голосов
/ 20 октября 2018

Как избежать необходимости разделять группы точкой с запятой

Используя идиоматический формат, используя отдельные строки.В конце концов, весь смысл DSL состоит в том, чтобы сделать код очень читабельным, показывая иерархическую структуру, и делая все в одной строке, отрицательно сказывается на цели:

val grp = group { 
    member { 
        name ("Bob") 
    }
    member { 
        name ("Sandy") 
    } 
}

Могу ли я добраться доиспользовать лямбду для задания имени вместо вызова функции?

Было бы логичнее и идиоматичнее удалить функцию имени и просто присвоить значение свойству:

name = "Bob"

Но да, вы также можете заменить свою функцию имени на

fun name(block: () -> String) {
    this.name = block()
}

и использовать

name {
    "Sandy"
}

Можно ли избежать того, чтобы имя было изменяемым в классе MEMBER?

Да: лямбда, передаваемая в функцию member (), настроит дополнительный класс MemberBuilder, который будет изменяемым, но позволит создать неизменный MEMBER:

fun group(create: GROUP.() -> Unit) = GROUP().apply(create)

class GROUP {
    private val members = mutableSetOf<MEMBER>()

    fun member(configure: MemberBuilder.() -> Unit) {
        val memberBuilder = MemberBuilder()
        memberBuilder.configure()
        members.add(memberBuilder.build())
    }

    override fun toString() = members.toString()

}

class MEMBER(val name: String) {
    override fun toString() = "MEMBER($name)"
}

class MemberBuilder {
    var name = "";

    fun build() = MEMBER(name)
}

fun main(args: Array<String>) {
    val grp = group {
        member {
            name = "Bob"
        }
        member {
            name = "Sandy"
        }
    }
    println(grp)
}

Также,обратите внимание, что классы, по соглашению, являются PascalCased, а не ALL_CAPS.

...