Koin Dependency Injection 在本地和远程数据源之间切换

Koin Dependency Injection switching between local and remote data source

所以我正在用远程数据源编写这个应用程序。我想添加本地数据库存储功能。我已经设置了一个架构,我有一个接口 DataSourceRemoteDataSourceLocalDataSource class 实现了该接口。 RemoteDataSource 注入了 ApiInterface 改造,LocalDataSource 注入了 DAO。

现在有了这个存储库接口和实现 SomeDataRepositorySomeDataRepositoryImpl。如果我希望存储库能够从 api 获取数据并将其保存到数据库,我该怎么做?

目前我已经将 RemoteDataSourceLocalDataSource class 都注入到 SomeDataRepositoryImpl 中以访问来自不同数据源的方法。这样我就可以调用 localDataSource.saveToDb() and/or remoteDatSource.fetchSomeData() int SomeRepositoryImpl class 之类的东西。但我不知道将具体实现传递给 class 是否可行。

但是如果我将单个 DataSource 接口传递给 SomeDataRepository,我将不得不在接口 DataSource 中定义一个 saveToDb() 函数,然后我也必须在 RemoteDataSource 中实施,这不是很好。

任何人都可以指导我了解此解决方案的最佳方法。

而且,当我在做的时候,在 api 接口中用 LiveData 包装器 class 包装数据以进行改造有什么好处吗?因为我不认为当在存储库上调用方法时,我想在存储库中观察它,然后访问数据以将其放入本地数据库。

由于您希望本地数据源充当远程数据源的回退,您可以根据需要创建另一个数据源实现,该实现是 composition of the local and remote data sources. This composite data source can contain the fallback logic and handle the delegation 到远程和本地数据源。一旦你这样做了,创建一个Koin模块来构建这些,并将复合数据源绑定到数据源接口是一件简单的事情。

假设这是您的界面和您已有的两个数据源:

interface DataSource {
    fun getData(): Data
}

class RemoteDataSource : DataSource {
    // ...
}

class LocalDataSource : DataSource {
    // ...
}

然后你可以像这样创建第三个实现:

class CompositeDataSource(
    val remote: RemoteDataSource, 
    val local: LocalDataSource
) : DataSource {
    override fun getData() : Data {
        return try {
            remote.getData()
        } catch (e: Exception) {
            local.getData()
        }
    }
}

要定义所有这些,您的 koin 模块将如下所示

module {
    single { RemoteDataSource() }
    single { LocalDataSource() }
    single<DataSource> { CompositeDataSource(remote = get(), local = get()) }
}

编辑:如果你真正想要的是一个缓存,你可以像这样使用本地数据源作为你的缓存:

class CompositeDataSource(
    val remote: RemoteDataSource, 
    val local: LocalDataSource
) : DataSource {
    override fun getData() : Data {
        return try {
            remote.getData().also { local.saveData(it) }
        } catch (e: Exception) {
            local.getData()
        }
    }
}

您可以尝试下一个方法,它只需很少的改动并且对我有用:

添加远程和本地数据源接口,应继承DataSource主接口

interface QuotesDataSource {

fun getQuotes(skip: Int = 0, force: Boolean = false): Flow<List<Quote>>

suspend fun updateQuotes(quotes: List<Quote>)
}

interface QuotesRemoteDataSource : QuotesDataSource

interface QuotesLocalDataSource : QuotesDataSource

然后使用这些接口创建 koin 模块

val repoModule = module {
  single { QuotesApi() }
  single<QuotesLocalDataSource> { QuotesDatabase(get()) }
  single<QuotesRemoteDataSource> { QuotesRemote(get()) }
  single<QuotesDataSource> {
      QuotesRepository(
          local = get<QuotesLocalDataSource>(),
          remote = get<QuotesRemoteDataSource>()
      )
  }
}