У меня есть система Parent-Search-Child, как показано ниже:
class Room
class Building {
fun find(by: By) = Room()
}
sealed class By {
abstract fun search(): Room
class ById(id: String) : By() {
override fun search(): Room = Room() // epic search method
}
class ByName(name: String) : By() {
override fun search(): Room = Room() // epic search method
}
class Byurpose(purpose: String) : By() {
override fun search(): Room = Room() // epic search method
}
companion object {
fun id(id: String) = ById(id)
fun name(name: String) = ByName(name)
fun purpose(purpose: String) = Byurpose(purpose)
}
}
Что можно использовать следующим образом:
val building = Building()
val room = building.find(By.name("Toilet"))
Однако я не очень доволен текущим синтаксисом, который может быть гораздо менее многословным в Kotlin. Кроме того, building.find
может появляться в коде тысяч раз. Я мог бы реализовать это по-другому, но на самом деле у меня нет классов Room
, Building
или By
, поэтому я не могу. Таков был мой подход:
Я реализовал класс контекста, в котором хранится ссылка Building
, и использую его внутри как источник для методов поиска:
class BuildingContext(private val building: Building) {
fun String.findById() = building.find(By.id(this))
fun String.findByName() = building.find(By.name(this))
fun String.findByPurpose() = building.find(By.purpose(this))
}
Может использоваться следующим образом:
with(BuildingContext(building)) {
val room2 = "Toilet".findByName()
}
После этого я заметил, что я использую только один метод поиска в 99% случаев, поэтому (ради еще более короткого синтаксиса!) Я реализовал следующие классы:
object AlwaysSearchById {
fun String.find(building: Building) = building.find(By.id(this))
}
object AlwaysSearchByName {
fun String.find(building: Building) = building.find(By.name(this))
}
object AlwaysSearchByPurpose {
fun String.find(building: Building) = building.find(By.purpose(this))
}
Что можно использовать таким образом:
with(AlwaysSearchByName) {
val room3 = "Toilet".find(building)
}
К сожалению, ссылка на здание появляется снова. Идеальный синтаксис был бы "Toilet".find()
. Я мог бы исправить это, повторно реализуя Always~
классы следующим образом:
class AlwaysSearchByNameV2(private val building: Building) {
fun String.find() = building.find(By.name(this))
}
И это будет использоваться, как показано ниже:
with(AlwaysSearchByNameV2(building)) {
val room = "Toilet".find()
}
Но в некоторых случаях я хотел бы также получить доступ к BuildingContext
методам, поэтому я должен написать:
with(BuildingContext(building)) {
with(AlwaysSearchByNameV2(building)) {
val toilet = "Toilet".find()
val randomRoom = "123".findById()
}
}
Вопрос в том, как сократить несколько with
предложений в этом случае?
В приведенном выше примере есть только 2 with
предложения, но это только базовый пример. В реальном мире их может быть десятки, и написание with(with(with(with(with...
наверняка будет болезненным.
На боковой ноте это не работает:
with(BuildingContext(building), AlwaysSearchByNameV2(building)) {
val toilet = "Toilet".find()
val randomRoom = "123".findById()
}
ни этот
with(*arrayOf(BuildingContext(building), BuildingContext(building))) {
val toilet = "Toilet".find()
val randomRoom = "123".findById()
}