Kotlin如何通过with子句组合不同的接收者而不重复

时间:2019-04-27 18:58:46

标签: search kotlin syntax parent-child kotlin-extension

我具有如下所示的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可以在代码中出现数千次。我可以以不同的方式实现它,但实际上我并不拥有RoomBuildingBy类,因此我没有。因此,这就是我的方法:

我实现了存储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()
}

1 个答案:

答案 0 :(得分:0)

您可以编写自定义范围函数,而不必始终依赖with。例如,您可以添加一个扩展功能,该功能将在AlwaysSearchByNameV2对象的范围内运行代码块:

inline fun BuildingContext.byName(f : AlwaysSearchByNameV2.() -> Unit) = AlwaysSearchByNameV2(building).apply(f)

并使用它:

with(BuildingContext(building)) {  // this: BuildingContext
    byName {  // this: AlwaysSearchByNameV2
        val toilet = "Toilet".find()
        val randomRoom = "123".findById()  // can still refer to BuildingContext
    }
    // back to this: BuildingContext
}