两个参数的动态绑定
Dynamic binding for two arguments
这个问题涉及静态类型语言中的设计模式,尤其是动态绑定(我在这里使用 Kotlin,但也可以是 C++ 或 Java)。问题如下:我有一个接口Node(表示一个Ast中的节点)和多个元素的串联(有多个这样的类)。
interface Node {
fun concat(next: Node): Node {
return Concat(listOf(this, next))
}
}
class Concat(val nodes: List<Node>): Node {
}
我现在想确保 Concat 始终是扁平的,即节点的 none 是一个串联。通过一些 if(next is Concat)
类型检查,这会很容易,但我想使用动态绑定并避免此类类型检查。我第一次失败的解决方案尝试如下:
interface Node {
fun concat(next: Node): Node {
return next.reverseConcat(this)
}
fun reverseConcat(prev: Node): Node {
return Concat(prev, this)
}
}
class Concat(val nodes: List<Node>): Node {
override fun concat(next: Node): Node {
// TODO what if next is a Concat?
return Concat(nodes + next)
}
override fun reverseConcat(prev: Node): Node {
// TODO what if prev is a Concat?
return Concat(listOf(prev) + nodes)
}
}
但如果两个节点都是 Concat 的实例,则此操作失败。另一种解决方案尝试是添加 reverseConcat
-方法,其中 Concat
作为参数。
interface Node {
// ...
fun reverseConcatWithConcat(nextNodes: List<Node>): Node {
return Concat(listOf(this) + nextNodes)
}
}
class Concat(val nodes: List<Node>): Node {
override fun concat(next: Node): Node {
return next.reverseConcatWithConcat(nodes)
}
override fun reverseConcat(prev: Node): Node {
// TODO what if prev is a Concat?
return Concat(listOf(prev) + nodes)
}
fun reverseConcatWithConcat(nextNodes: List<Node>): Node {
return Concat(nodes + nextNodes)
}
}
这可行,但它会使界面混乱(考虑到还有其他节点,类似于 Concat),并且还留下了界面中没有受保护方法的问题,因此 reverseConcat 仍然很危险。
是否有更令人满意的使用动态绑定的方法,不会不必要地使代码混乱?
通常我会做这样的事情,因为它是高效的(尽管我会提供 multi-concat):
interface Node {
fun forContents(proc: (Node) -> Unit) {
proc(this)
}
// I don't actually like this signature, but it's what you wanted
fun concat(next: Node) : Node {
val list = ArrayList<Node>()
forContents(list::add)
next.forContents(list::add)
return Concat(list)
}
}
class Concat(val contents: List<Node>) : Node {
override fun forContents(proc: (Node) -> Unit) {
contents.forEach(proc)
}
}
您也可以这样做:
interface Node {
val contents: List<Node> get() = listOf(this)
fun concat(next: Node) : Node = Concat(
listOf(this.contents, next.contents).flatten()
)
}
class Concat(override val contents: List<Node>) : Node {
}
这个问题涉及静态类型语言中的设计模式,尤其是动态绑定(我在这里使用 Kotlin,但也可以是 C++ 或 Java)。问题如下:我有一个接口Node(表示一个Ast中的节点)和多个元素的串联(有多个这样的类)。
interface Node {
fun concat(next: Node): Node {
return Concat(listOf(this, next))
}
}
class Concat(val nodes: List<Node>): Node {
}
我现在想确保 Concat 始终是扁平的,即节点的 none 是一个串联。通过一些 if(next is Concat)
类型检查,这会很容易,但我想使用动态绑定并避免此类类型检查。我第一次失败的解决方案尝试如下:
interface Node {
fun concat(next: Node): Node {
return next.reverseConcat(this)
}
fun reverseConcat(prev: Node): Node {
return Concat(prev, this)
}
}
class Concat(val nodes: List<Node>): Node {
override fun concat(next: Node): Node {
// TODO what if next is a Concat?
return Concat(nodes + next)
}
override fun reverseConcat(prev: Node): Node {
// TODO what if prev is a Concat?
return Concat(listOf(prev) + nodes)
}
}
但如果两个节点都是 Concat 的实例,则此操作失败。另一种解决方案尝试是添加 reverseConcat
-方法,其中 Concat
作为参数。
interface Node {
// ...
fun reverseConcatWithConcat(nextNodes: List<Node>): Node {
return Concat(listOf(this) + nextNodes)
}
}
class Concat(val nodes: List<Node>): Node {
override fun concat(next: Node): Node {
return next.reverseConcatWithConcat(nodes)
}
override fun reverseConcat(prev: Node): Node {
// TODO what if prev is a Concat?
return Concat(listOf(prev) + nodes)
}
fun reverseConcatWithConcat(nextNodes: List<Node>): Node {
return Concat(nodes + nextNodes)
}
}
这可行,但它会使界面混乱(考虑到还有其他节点,类似于 Concat),并且还留下了界面中没有受保护方法的问题,因此 reverseConcat 仍然很危险。
是否有更令人满意的使用动态绑定的方法,不会不必要地使代码混乱?
通常我会做这样的事情,因为它是高效的(尽管我会提供 multi-concat):
interface Node {
fun forContents(proc: (Node) -> Unit) {
proc(this)
}
// I don't actually like this signature, but it's what you wanted
fun concat(next: Node) : Node {
val list = ArrayList<Node>()
forContents(list::add)
next.forContents(list::add)
return Concat(list)
}
}
class Concat(val contents: List<Node>) : Node {
override fun forContents(proc: (Node) -> Unit) {
contents.forEach(proc)
}
}
您也可以这样做:
interface Node {
val contents: List<Node> get() = listOf(this)
fun concat(next: Node) : Node = Concat(
listOf(this.contents, next.contents).flatten()
)
}
class Concat(override val contents: List<Node>) : Node {
}