jeudi 23 juillet 2020

Dynamic binding for two arguments

This question concerns design patterns, and in particular dynamic binding, in statically typed languages (I use Kotlin here, but it could also be C++ or Java). The problem is as follows: I have an interface Node (representing nodes in an Ast) and concatenation of multiple elements (there are multiple such classes).

interface Node {
    fun concat(next: Node): Node {
        return Concat(listOf(this, next))
    }
}

class Concat(val nodes: List<Node>): Node {
}

I now want to make sure, that Concat is always flattened, ie, none of the nodes is a concatenation. This would be easy with some if(next is Concat) type check, but I would like to use dynamic binding and avoid such type checks. My first failing solution attempt would be the following:

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) 
    }
}

but this fails if both nodes are instances of Concat. Another solution attempt would be to add reverseConcat-methods with Concat as parameters.

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)
    }
}

This would work but it clutters the interface (consider that there are other nodes, similar to Concat) and it also leaves the problem that there are no protected methods in interfaces so that reverseConcat is still dangerous.

Is there a more satisfying approach using dynamic binding that does not clutter the code unnecessarily?

Aucun commentaire:

Enregistrer un commentaire