When I've exceeded 20 inputs for the view model, m...
# ballast
r
When I've exceeded 20 inputs for the view model, my InputHandler class became too large to manage comfortably. It's not trivial to partition the class into smaller pieces. I've tried to split my view model, but it was a dead end (my state class is not large and is really shared by most of those inputs).
I've came up with this pattern:
Copy code
object RaportContract {
    data class State(
       // ...
    )

    sealed class Inputs {

        sealed class KolumnyInputs : Inputs() {
            // first group of inputs
        }

        sealed class WarunkiInputs : Inputs() {
            // second group of inputs
        }
    }
    
    sealed class Events
}

// RaportInputHandler.kt

class RaportInputHandler() :
    InputHandler<RaportContract.Inputs, RaportContract.Events, RaportContract.State> {
    override suspend fun InputHandlerScope<RaportContract.Inputs, RaportContract.Events, RaportContract.State>.handleInput(
        input: RaportContract.Inputs
    ) = when (input) {

        is KolumnyInputs -> {
            raportKolumnyHandleInput(input)
        }

        is WarunkiInputs -> {
            raportWarunkiHandleInput(input)
        }
    }
}

// RaportKolumnyHandle.kt

suspend fun InputHandlerScope<RaportContract.Inputs, RaportContract.Events, RaportContract.State>.raportKolumnyHandleInput(
    input: RaportContract.Inputs.KolumnyInputs
): Unit = when (input) {
    // handle first group
}

// RaportWarunkiHandle.kt

suspend fun InputHandlerScope<RaportContract.Inputs, RaportContract.Events, RaportContract.State>.raportWarunkiHandleInput(
    input: RaportContract.Inputs.WarunkiInputs): Unit = when (input) {
    // handle second group
}
Seems to work fine. What do you think?
c
Yeah, I think that looks like a great approach. A good use of the type system to organize things without introducing much additional boilerplate. Another potential solution that comes to mind (that I wouldn’t necessarily recommend in interest of being opinionated, but definitely isn’t wrong and might work for you) would be to have each Input override a method and handle itself within the class body, rather than in the InputHandler. You can then split the Inputs into different files to help with organization as needed. I don’t personally like this approach because it makes it more difficult to see all the Inputs available for a given VM, but it may be a better approach for some situations, and in fact is how the Router’s VM is set up.
Copy code
// RaportContract.kt
typealias RaportInputScope = InputHandlerScope<RaportContract.Inputs, RaportContract.Events, RaportContract.State>

object RaportContract {
    data class State(
       // ...
    )

    abstract class Inputs {
        abstract fun RaportInputScope.handle()
    }
    
    sealed class Events
}

// each in their own files
class KolumnyInputs1 : Inputs() {
    override fun RaportInputScope.handle() { 
    }
}

class KolumnyInputs2 : Inputs() {
    override fun RaportInputScope.handle() { 
    }
}

class WarunkiInputs1 : Inputs() {
    override fun RaportInputScope.handle() { 
    }
}

class WarunkiInputs2 : Inputs() {
    override fun RaportInputScope.handle() { 
    }
}

// RaportInputHandler.kt
class RaportInputHandler() : InputHandler<RaportContract.Inputs, RaportContract.Events, RaportContract.State> {
    override suspend fun RaportInputScope.handleInput(
        input: RaportContract.Inputs
    ) = with(input) {
        handle()
    }
}