3
\$\begingroup\$

Background

In my development of a game engine, I realized the need to be able to construct processing units in a serializable way (code below does not show serialization, just the wiring).

I broke the problem down into nodes and connectors. A Node is the processing unit. A Node may have zero or more connectors, for transmitting signals, and zero or more functions, for receiving transmitted signals.

This is written as a library, so trying to follow the additional steps of explicit access modifiers and return types.

A NodeConnector has two components, a transmitter, and a receiver. The transmitter should stay within the walls of the node

The code is also written so that it's focused on dependencies only. I'm trying to make this as reactive and stateless as possible. So the nodes should specify the things that they can be "attached to", and the way other nodes can be "attached to" them, but not all of the things attached to them.

Source

Node Connector

/* NodeConnector.kt
 * Copyright (C) 2019 Zymus
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package organicode

public interface NodeConnector<T : Any> {

    public val transmitter: NodeSignalTransmitter<T>

    public val receiver: NodeSignalReceiver<T>
}

public interface NodeSignalTransmitter<T : Any> {

    public fun transmit(signal: T): Unit
}

public interface NodeSignalReceiver<T : Any> {

    public fun onSignal(onSignal: (signal: T) -> Unit): Unit
}

public fun <T : Any> nodeConnector(): NodeConnector<T> {
    return object : NodeConnector<T>, NodeSignalTransmitter<T>, NodeSignalReceiver<T> {

        public override val transmitter: NodeSignalTransmitter<T>
            get() = this

        public override val receiver: NodeSignalReceiver<T>
            get() = this

        private var _onSignal: (T) -> Unit = {}

        public override fun onSignal(onSignal: (signal: T) -> Unit): Unit {
            _onSignal = onSignal
        }

        public override fun transmit(signal: T): Unit {
            _onSignal(signal)
        }
    }
}

Value Transmitter Node

/* ValueTransmitterNode.kt
 * Copyright (C) 2019 Zymus
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package organicode

public interface ValueTransmitterNode<T : Any> {

    public val value: NodeSignalReceiver<T>

    public fun transmit()
}

public fun <T : Any> valueTransmitter(value: T): ValueTransmitterNode<T> {
    return object : ValueTransmitterNode<T> {

        public override val value: NodeSignalReceiver<T>
            get() = valueTransmitter.receiver

        private val valueTransmitter: NodeConnector<T> = nodeConnector()

        public override fun transmit(): Unit {
            valueTransmitter.transmitter.transmit(value)
        }
    }
}

Transform Node

/* TransformNode.kt
 * Copyright (C) 2019 Zymus
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package organicode

public interface TransformNode<T : Any, R : Any> {

    public val transformedValue: NodeSignalReceiver<R>

    public fun transformValueFrom(receiver: NodeSignalReceiver<T>): Unit
}

public fun <T : Any, R : Any> transformNode(transform: (T) -> R): TransformNode<T, R> {
    return object : TransformNode<T, R> {

        public override val transformedValue: NodeSignalReceiver<R>
            get() = transformedValueConnector.receiver

        private val transformedValueConnector: NodeConnector<R> = nodeConnector()

        public override fun transformValueFrom(receiver: NodeSignalReceiver<T>): Unit {
            receiver.onSignal {
                transformedValueConnector.transmitter.transmit(transform(it))
            }
        }
    }
}

Predicate Node

/* PredicateNode.kt
 * Copyright (C) 2019 Zymus
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package organicode

public interface PredicateNode<T : Any> {

    public val onTrue: NodeSignalReceiver<T>

    public fun testSignalFrom(receiver: NodeSignalReceiver<T>): Unit
}

public fun <T : Any> predicateNode(predicate: (T) -> Boolean): PredicateNode<T> {
    return object : PredicateNode<T> {

        public override val onTrue: NodeSignalReceiver<T>
            get() = onTrueConnector.receiver

        private val onTrueConnector: NodeConnector<T> = nodeConnector()

        public override fun testSignalFrom(receiver: NodeSignalReceiver<T>) {
            receiver.onSignal {
                if (predicate(it)) {
                    onTrueConnector.transmitter.transmit(it)
                }
            }
        }
    }
}

Print Node

/* PrintNode.kt
 * Copyright (C) 2019 Zymus
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package organicode

public interface PrintNode<T : Any> {

    public fun printMessageFrom(receiver: NodeSignalReceiver<T>, messageSupplier: (signal: T) -> String = { it.toString() }): Unit
}

public fun <T : Any> printNode(): PrintNode<T> {
    return object : PrintNode<T> {

        public override fun printMessageFrom(receiver: NodeSignalReceiver<T>, messageSupplier: (signal: T) -> String): Unit {
            receiver.onSignal { println(messageSupplier(it)) }
        }
    }
}

Test

/* NodeTests.kt
 * Copyright (C) 2019 Zymus
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package organicode

import org.junit.Assert.assertEquals
import org.junit.Test

public class NodeTests {

    @Test
    fun `transform value and print if match`() {
        val nameTransmitter: ValueTransmitterNode<String> = valueTransmitter("Reno")
        val transformNode: TransformNode<String, String> = transformNode(String::toUpperCase)
        val predicateNode: PredicateNode<String> = predicateNode("RENO"::equals)
        val printNode: PrintNode<String> = printNode()

        transformNode.transformValueFrom(nameTransmitter.value)
        predicateNode.testSignalFrom(transformNode.transformedValue)
        var printNodeHit: Boolean = false
        printNode.printMessageFrom(predicateNode.onTrue) {
            printNodeHit = true
            "name: $it"
        }

        nameTransmitter.transmit()
        assertTrue(printNodeHit)
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.