Hi, Im working on developing an Android applicatio...
# android
t
Hi, Im working on developing an Android application based on MVI architecture. Ive attempted to model User Interactions as Action, Reactions and Consequences and come up with the following interfaces and sealed classes
đź§µ 2
interface Interaction<Input, Output> {
interface Reaction<Output> : Interaction<Nothing, Output> {
val output: Output
}
interface Action<Input, Output> : Interaction<Input, Output> {
val input: Input
fun react(output: Output): Consequence<Class<out Action<Input, Output>>, Reaction<Output>>
}
}
data class Consequence<out K : Class<out Interaction.Action<*, *>>, out R : Interaction.Reaction<*>>(
val klass: K,
val reaction: R
)
To test this out I have developed the following data classes and sealed classes
data class CustomInput(val whatever: String, val something: Double)
data class CustomInputTwo(val kotlin: String, val future: Double, val space: Int)
data class CustomOutput(val frank: Long, val homer: String)
data class CustomOutputTwo(val geezer: Long, val su: String)
sealed class Interactions<I, O> : Interaction<I, O> {
data class ActionOne(override val input: String) : Interaction.Action<String, Long> {
private data class ReactionOne(override val output: Long) : Interaction.Reaction<Long>
override fun react(output: Long): Consequence<Class<out Interaction.Action<String, Long>>, Interaction.Reaction<Long>> =
Consequence(this@ActionOne::class.java, ReactionOne(output))
}
data class ActionTwo(override val input: CustomInput) : Interaction.Action<CustomInput, CustomOutput> {
private data class ReactionTwo(override val output: CustomOutput) : Interaction.Reaction<CustomOutput>
override fun react(output: CustomOutput): Consequence<Class<out Interaction.Action<CustomInput, CustomOutput>>, Interaction.Reaction<CustomOutput>> =
Consequence(this@ActionTwo::class.java, ReactionTwo(output))
}
data class ActionThree(override val input: Nothing? = null) : Interaction.Action<Nothing?, CustomOutputTwo> {
private data class ReactionThree(override val output: CustomOutputTwo) : Interaction.Reaction<CustomOutputTwo>
override fun react(output: CustomOutputTwo): Consequence<Class<out Interaction.Action<Nothing?, CustomOutputTwo>>, Interaction.Reaction<CustomOutputTwo>> =
Consequence(this@ActionThree::class.java, ReactionThree(output))
}
data class ActionFour(override val input: Nothing? = null) : Interaction.Action<Nothing?, Nothing?> {
private data class ReactionFour(override val output: Nothing? = null) : Interaction.Reaction<Nothing?>
override fun react(output: Nothing?): Consequence<Class<out Interaction.Action<Nothing?, Nothing?>>, Interaction.Reaction<Nothing?>> =
Consequence(this@ActionFour::class.java, ReactionFour())
}
data class ActionFive(override val input: CustomInputTwo) : Interaction.Action<CustomInputTwo, Nothing?> {
private data class ReactionFive(override val output: Nothing? = null) : Interaction.Reaction<Nothing?>
override fun react(output: Nothing?): Consequence<Class<out Interaction.Action<CustomInputTwo, Nothing?>>, Interaction.Reaction<Nothing?>> =
Consequence(this@ActionFive::class.java, ReactionFive())
}
}
This approach works however I would appreciate any feed back on how I could improve these classes Especially how I could make the sealed class
Interactions
more succinct The features I would really like to factor out is having to use
Nothing?
Copy code
fun main() = runBlocking {

    val controller = PrototypeViewModel()

    val interactions =
        listOf(
            Interactions.ActionOne("hi"),
            Interactions.ActionTwo(CustomInput(whatever = "Really???", something = Math.PI)),
            Interactions.ActionThree(),
            Interactions.ActionFour(),
            Interactions.ActionFive(CustomInputTwo(kotlin = "code", space = 8765, future = Math.E)),
        )

    interactions.forEach { action ->
        controller.call(action).collect { consequence ->
            when (consequence.klass) {
                Interactions.ActionOne::class.java -> println("\t\t ${consequence.reaction.output}")
                Interactions.ActionTwo::class.java -> println("\t\t ${consequence.reaction.output}")
                Interactions.ActionThree::class.java -> println("\t\t ${consequence.reaction.output}")
                Interactions.ActionFour::class.java -> println("\t\t ${consequence.reaction.output}")
                Interactions.ActionFive::class.java -> println("\t\t ${consequence.reaction.output}")
                else -> TODO("Noooooooooooooooo!!!!!!!!!!!!!")
            }

        }
    }
}
z
you've just invented functions of an interface with 4x the amount of code, please use interfaces instead of this MVI variant
t
thanks for taking the time to review my code snippet and for your suggestion... could you elaborate of what you envisage is an improvemnet on my approach please
In further explanation of my goal… I am attempting to define a family of classes with the following relationships Each request has an associated response. I wish to enforce strict rules that only allow each unique response to be associated with its Parent request, for example Request1 can only ever produce Response1, Response2 can only ever produce Response2. In other words Request1 can never produce Response2 I have one other important requirement, The declarations of each Android Activities Request/Response pairs are capable of being written succinctly at the top of each activity class file and at a glance any maintainer of the code can see what functions are available from the activities UI
@zhuinden EpicPandaForce, I do not understand how an interface alone could give me the desired solution, could uyo please give me a "clue"
z
Action is replaceable with method call over interface, reaction is replaceable with callback. If a state machine is needed, that can be a self-contained component that is used as private implementation detail. This attempt at generalization of this logic will most likely provide you no positive value.