我具有如下所示的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()
}
答案 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
}