如何创建Kotlin泛型工厂生成子类,减少ViewModel样板代码?
解决方案
一、通用泛型基工厂实现
针对多数ViewModel仅需传入arguments的场景,实现通用工厂类避免重复编写样板代码:
open class GenericViewModelFactory<VM : BaseViewModel<T>, T : BaseViewModel.In>( private val args: T, private val creator: (T) -> VM ) : BaseViewModel.Factory<VM>() { override fun get(): VM = creator(args) } // 配合inline reified函数自动获取构造函数,进一步简化调用 inline fun <reified VM : BaseViewModel<T>, reified T : BaseViewModel.In> genericFactory(args: T): GenericViewModelFactory<VM, T> { val constructor = VM::class.primaryConstructor ?: error("ViewModel必须定义主构造函数") return GenericViewModelFactory(args) { constructor.call(it) } }
使用时子类无需再内部编写Factory,直接通过genericFactory获取实例即可。
二、统一伴生对象Factory调用方式
通过接口约定实现子类伴生对象的Factory入口统一:
修改BaseViewModel
abstract class BaseViewModel<T : BaseViewModel.In>(val arguments: T) : ViewModel() { interface In abstract class Factory<X : BaseViewModel<*>> : ViewModelProvider.Factory { abstract fun get(): X override fun <U : ViewModel> create( modelClass: KClass<U>, extras: CreationExtras ): U { return get() as U } } // 定义工厂提供者接口,子类伴生对象需实现此接口 interface ViewModelFactoryProvider<VM : BaseViewModel<T>, T : BaseViewModel.In> { fun createFactory(args: T): Factory<VM> } companion object { inline fun <reified VM : BaseViewModel<T>, reified T : BaseViewModel.In> factory(args: T): Factory<VM> { val provider = VM::class.companionObjectInstance as? ViewModelFactoryProvider<VM, T> ?: error("${VM::class.simpleName}的伴生对象必须实现ViewModelFactoryProvider接口") return provider.createFactory(args) } } }
子类实现
class ExampleVM(arguments: In) : BaseViewModel<ExampleVM.In>(arguments) { @Serializable data class In(val number: Int) : BaseViewModel.In companion object : BaseViewModel.ViewModelFactoryProvider<ExampleVM, In> { override fun createFactory(args: In): BaseViewModel.Factory<ExampleVM> { // 直接复用通用工厂 return genericFactory(args) } } }
现在可通过ExampleVM.factory(arguments)直接获取对应Factory,统一调用方式。
三、Composable路由逻辑封装
将参数解析、ViewModel创建的重复逻辑封装为通用inline函数:
inline fun <reified Route : BaseViewModel.In, reified VM : BaseViewModel<Route>> composableWithViewModel( crossinline content: @Composable (VM) -> Unit ) { composable<Route> { backStackEntry -> val arguments = backStackEntry.toRoute<Route>() val vm: VM = viewModel( factory = VM.factory(arguments), key = arguments.toString() ) content(vm) } }
使用示例
composableWithViewModel<ExampleVM.In, ExampleVM> { vm -> ExampleVMView(viewModel = vm) }
内容的提问来源于stack exchange,提问作者Felipe Ribeiro R. Magalhaes
相关产品推荐
相关产品推荐

