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
相关产品推荐
相关产品推荐

