Я пытаюсь найти идеальное решение моей проблемы с областью, и мне бы очень хотелось, чтобы вы высказали свое мнение.
У меня есть некоторые сторонние классы, которые я не могу изменить:
class Employee {
var id = 0
var name = ""
var card : Card? = null
// ...
}
class Card {
var cardId = 0
}
МойЦель состоит в том, чтобы иметь возможность построить Employee следующим образом:
val built = employee {
id = 5
name = "max"
addCard {
cardId = 5
}
}
В исходных компонентах нет метода addCard .Поэтому я придумал следующий компоновщик:
@DslMarker
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE)
annotation class Scoped
@Scoped
object Builder {
inline fun employee (init: (@Scoped Employee).() -> Unit): Employee {
val e = Employee()
e.init()
return e
}
inline fun Employee.addCard(init: (@Scoped Card).() -> Unit) {
val c = Card()
c.init()
card = c
}
}
К сожалению, теперь я получаю печально известную ошибку:
error: 'inline fun Employee.addCard (init: (Scratch_1.Card). () -> Unit): Unit 'не может быть вызван в этом контексте неявным получателем.При необходимости используйте явное
Я понимаю причину ошибки и хотел бы подумать о ее решениях.
Удалите аннотацию DSLMarker, чтобыв состоянии наследовать родительскую область.К сожалению, это допускает нелегальное использование компоновщика:
with(Builder) {
val built = employee {
id = 5
name = "max"
addCard {
employee {
// ...
}
cardId = 5
}
}
}
Используйте квалифицированное this для доступа к родительской области.Но тогда мы должны использовать другого квалифицированного это, чтобы получить правильный приемник.Это довольно многословно.
with(Builder) {
val built = employee {
id = 5
name = "max"
with(this@with) {
this@employee.addCard {
cardId = 5
}
}
}
}
Унаследовать сотрудника, чтобы иметь возможность добавить в него функцию расширения (делегирование здесь невозможно, потому что у меня много свойств в Employee,и там не все определяется интерфейсом).Это не всегда может работать, если класс третьей стороны является окончательным.
class EmployeeEx : Employee() {
inline fun addCard(init: (@Scoped Card).() -> Unit) {
val c = Card()
c.init()
card = c
}
}
и строитель:
@Scoped
object Builder {
inline fun employee (init: (@Scoped EmployeeEx).() -> Unit): Employee {
val e = EmployeeEx()
e.init()
return e
}
}
Так что является лучшим решением??Я что-то пропустил ?Большое спасибо за чтение всего этого!