两个参数的动态绑定

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 {

}