Вы можете попробовать использовать параметры корневого узла и компоновщика:
sealed class Node {
data class Root(
val createChildren: ParentList.() -> Unit
) : Node() {
val children: List<Node> = ParentList(this).apply(createChildren)
}
data class Branch(
val createChildren: ParentList.() -> Unit,
val parent: Node
) : Node() {
val children: List<Node> = ParentList(this).apply(createChildren)
}
data class Leaf(
val parent: Node
) : Node()
}
class ParentList(
val parent: Node,
private val children: MutableList<Node> = mutableListOf()
) : List<Node> by children {
fun branch(createChildren: ParentList.() -> Unit) {
children += Node.Branch(createChildren, parent)
}
fun leaf() {
children += Node.Leaf(parent)
}
}
fun root(createChildren: ParentList.() -> Unit) = Node.Root(createChildren)
Они могут быть построены следующим образом (возможно, потребуется добавить дополнительные детали к любому из узлов):
fun usage() {
val graph = root {
branch {
leaf()
}
branch {
branch {
leaf()
}
leaf()
}
}
}
Вы можете разрешить доступ потенциальным детям и / или родителям со свойствами расширения:
val Node.children: List<Node> get() = when(this) {
is Node.Root -> children
is Node.Branch -> children
is Node.Leaf -> emptyList()
}
val Node.parent: Node? get() = when(this) {
is Node.Root -> null
is Node.Branch -> parent
is Node.Leaf -> parent
}
Итак, вы можете перемещаться по потомкам:
fun Node.allChildren(): List<Node> =
children + children.flatMap { it.allChildren() }
Или перейти вверх:
fun Node.allParents(): List<Node> =
listOfNotNull(parent).flatMap { listOf(it) + allParents() }
Чтобы избежать оценки при выполнении поиска, который может закончиться раньше, вы всегда можете использовать последовательности вместо списков:
fun Node.allChildren(): Sequence<Node> =
children.asSequence() + children.asSequence().flatMap { it.allChildren() }
fun Node.allParents(): Sequence<Node> =
listOfNotNull(parent).asSequence().flatMap { sequenceOf(it) + it.allParents() }
Примечание: Остерегайтесь Stackoverflows