Циркулярная зависимость (Voronoi Halfedge)

Я знаю, что «круговая зависимость – плохой дизайн», но я думаю, что в этом случае это оправданно.

При построении диаграммы voronoi ячейки делятся на так называемые «половинные края», это позволяет вам удобно перемещать диаграмму.

Во всяком случае, чтобы создать экземпляр полузадачи, я должен указать зеркало или двойник полузадачи.

Это фанки на любом языке, но в Kotlin это еще более раздражает, потому что я должен использовать nullable var вместо val, как я бы предпочел.

Прямо сейчас я делаю эту мелодию, которая мне не нравится;

val mirrorEdge: HalfEdge get() = halfEdge!! private var halfEdge: HalfEdge? = null fun setMirror(halfEdge: HalfEdge) { this.halfEdge = halfEdge } 

// в другом месте

 newEdge.setMirror(newEdge2) newEdge2.setMirror(newEdge) 

Зеркальное зеркало не может быть нулевым и должно быть неизменным, но я не вижу, как сообщить это намерение в моем коде.

Не видя полного определения HalfEdge это может не сработать, но рассмотрим следующее:

 interface HalfEdge { val mirrorHalf: HalfEdge } class HalfEdges { private inner class First : HalfEdge { override val mirrorHalf: HalfEdge get() = second } private inner class Second : HalfEdge { override val mirrorHalf: HalfEdge get() = first } val first: HalfEdge = First() val second: HalfEdge = Second() operator fun component1() = first operator fun component2() = second } 

Применение:

 val (newEdge, newEdge2) = HalfEdges() check(newEdge == newEdge2.mirrorHalf) check(newEdge.mirrorHalf == newEdge2) 

Вы должны одновременно создать обе половины, и хотя вы никогда не сможете ссылаться на нее, оба полуребра сохраняют ссылку на весь их крайний контейнер, чтобы они могли обращаться друг к другу. Вышеупомянутая реализация может быть легко адаптирована для передачи данных на два полуребра и / или связывания данных со всем краем.

Этот же принцип может быть реализован также с родительским классом «График» или «Сеть» с внутренней двунаправленной картой, грани которой зеркало друг к другу.