You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android领域层结合Room Dao与Repository的依赖优化方案问询

问题描述

我的项目已经到了需要实现领域层的阶段,参考Android官方文档,我实现了FormatDateUseCase:

class FormatDateUseCase(userRepository: UserRepository) {

    private val formatter = SimpleDateFormat(
        userRepository.getPreferredDateFormat(),
        userRepository.getPreferredLocale()
    )
    
    operator fun invoke(date: Date): String {
        return formatter.format(date)
    }
}

随后在ViewModel中调用该用例:

class MyViewModel(formatDateUseCase: FormatDateUseCase) : ViewModel() {
    init {
        val today = Calendar.getInstance()
        val todaysDate = formatDateUseCase(today)
        /* ... */
    }
}

而我接触到的Room示例里,UserRepository的定义如下:

class UserRepository(private val userDao: UserDao) {

    fun insertUser(user: User) {
        userDao.insert(user)
    }
}

这导致FormatDateUseCase间接依赖由Room数据库实例化的UserDao。目前我通过嵌套实例化的方式创建ViewModel:

val viewModel: MyViewModel = MyViewModel(
    FormatDateUseCase(
        UserRepository(
            myRoomDatabase.userDao
        )
    )
)

请问有没有更简洁的实现方式?


优化方案

1. 使用依赖注入框架(推荐)

Android生态中最常用的是Hilt,它能自动处理依赖的实例化与注入,彻底消除手动嵌套创建对象的繁琐。

具体实现步骤:

  • 给UserRepository、FormatDateUseCase添加Hilt注入注解:
    // 标记Repository为可注入类
    @Inject
    class UserRepository(private val userDao: UserDao) {
        fun insertUser(user: User) {
            userDao.insert(user)
        }
        
        // 假设你已实现这两个获取用户偏好的方法
        fun getPreferredDateFormat(): String = /* 实现逻辑 */
        fun getPreferredLocale(): Locale = /* 实现逻辑 */
    }
    
    // 给UseCase的构造方法添加@Inject注解
    class FormatDateUseCase @Inject constructor(private val userRepository: UserRepository) {
        private val formatter = SimpleDateFormat(
            userRepository.getPreferredDateFormat(),
            userRepository.getPreferredLocale()
        )
        
        operator fun invoke(date: Date): String {
            return formatter.format(date)
        }
    }
    
  • 给ViewModel添加@HiltViewModel注解,通过构造方法注入UseCase:
    @HiltViewModel
    class MyViewModel @Inject constructor(
        private val formatDateUseCase: FormatDateUseCase
    ) : ViewModel() {
        init {
            // 注意:Calendar.getInstance()需转成Date类型,原代码直接传Calendar会报错
            val today = Calendar.getInstance().time
            val todaysDate = formatDateUseCase(today)
            /* ... */
        }
    }
    
  • 在Activity/Fragment中直接通过ViewModelProvider获取ViewModel:
    val viewModel: MyViewModel = ViewModelProvider(this)[MyViewModel::class.java]
    

Hilt会自动完成UserDao、UserRepository、FormatDateUseCase的实例化与注入,无需手动维护依赖链。

2. 手动封装依赖创建逻辑(无框架场景)

如果暂时不想引入依赖注入框架,可以把所有依赖的创建逻辑集中到一个工具类中,比如DependencyProvider:

object DependencyProvider {
    // 假设myRoomDatabase是已初始化的单例数据库实例
    private val userDao = myRoomDatabase.userDao
    
    private val userRepository = UserRepository(userDao)
    
    private val formatDateUseCase = FormatDateUseCase(userRepository)
    
    fun provideMyViewModel(): MyViewModel {
        return MyViewModel(formatDateUseCase)
    }
}

后续创建ViewModel只需调用:

val viewModel = DependencyProvider.provideMyViewModel()

所有依赖的创建都集中在一处,后续修改依赖关系时只需改动这个类,避免分散修改的麻烦。

3. 额外优化:FormatDateUseCase的设计

原代码中SimpleDateFormat在构造方法中初始化,若用户偏好设置发生变化,Formatter无法自动更新。可以调整为两种方案:

  • 每次调用时获取最新偏好(适合偏好频繁变化的场景):
    class FormatDateUseCase @Inject constructor(private val userRepository: UserRepository) {
        operator fun invoke(date: Date): String {
            val formatter = SimpleDateFormat(
                userRepository.getPreferredDateFormat(),
                userRepository.getPreferredLocale()
            )
            return formatter.format(date)
        }
    }
    
  • 结合懒加载与偏好监听(适合偏好极少变化的场景):
    class FormatDateUseCase @Inject constructor(private val userRepository: UserRepository) {
        private var formatter: SimpleDateFormat? = null
        
        init {
            // 监听用户偏好变化,更新formatter
            observeUserPreferences {
                formatter = SimpleDateFormat(it.dateFormat, it.locale)
            }
        }
        
        operator fun invoke(date: Date): String {
            val currentFormatter = formatter ?: run {
                SimpleDateFormat(
                    userRepository.getPreferredDateFormat(),
                    userRepository.getPreferredLocale()
                ).also { formatter = it }
            }
            return currentFormatter.format(date)
        }
        
        // 假设你有监听偏好变化的方法
        private fun observeUserPreferences(callback: (UserPreferences) -> Unit) {
            /* 实现逻辑 */
        }
    }
    

内容的提问来源于stack exchange,提问作者Ne0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 11:31:04