简体   繁体   English

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

[英]Koin Dependency Injection switching between local and remote data source

So I am writing this app with remote data source.所以我正在用远程数据源编写这个应用程序。 And I wanted to add local db storage capabilities.我想添加本地数据库存储功能。 I have setup an architecture whereby I have an interface DataSource .我已经设置了一个架构,我有一个接口DataSource A RemoteDataSource and a LocalDataSource classes implement that interface. RemoteDataSourceLocalDataSource类实现该接口。 The RemoteDataSource is injected with ApiInterface retrofit and the LocalDataSource is injected with DAOs. RemoteDataSource注入了ApiInterface retrofit, LocalDataSource注入了 DAO。

Now there is this repository interface and implementation SomeDataRepository and SomeDataRepositoryImpl .现在有这个存储库接口和实现SomeDataRepositorySomeDataRepositoryImpl If I want the repository to be able to fetch the data from api and save it to the database, how do I go about doing that?如果我希望存储库能够从 api 获取数据并将其保存到数据库中,我 go 应该怎么做呢?

Currently I have injected both RemoteDataSource and LocalDataSource classes to the SomeDataRepositoryImpl to access methods from different data sourcces.目前,我已将RemoteDataSourceLocalDataSource类都注入到SomeDataRepositoryImpl中,以访问来自不同数据源的方法。 This way I can call something like localDataSource.saveToDb() and/or remoteDatSource.fetchSomeData() int SomeRepositoryImpl class. But I do not know if passing concrete implementations to a class is the way to go.这样我就可以调用localDataSource.saveToDb()和/或remoteDatSource.fetchSomeData() int SomeRepositoryImpl class 之类的东西。但我不知道将具体实现传递给 class 是否是通往 go 的途径。

But if I pass lets say a single DataSource interface to the SomeDataRepository , I will have to define a saveToDb() function int the interface DataSource and then I will have to implement that in RemoteDataSource as well which is not that good.但是,如果我将单个DataSource接口传递给SomeDataRepository ,我将必须在接口DataSource中定义一个saveToDb() function int,然后我还必须在RemoteDataSource中实现它,这不是很好。

Can anyone please guide me through what the best approach is to this solution.谁能指导我解决此解决方案的最佳方法。

And also while I am at it, is it any good to wrap the data with LiveData wrapper class right in the api interface for retrofit?而且当我在做的时候,在 retrofit 的 api 接口中使用LiveData包装器 class 包装数据有什么好处吗? cause I dont think when a method is called on the repository, I would want to observe it right there in the repo and then access the data to put it onto local db.因为我不认为当在存储库上调用方法时,我想在存储库中观察它,然后访问数据以将其放入本地数据库。

Since you want to have the local data source act as a fallback for the remote data source, you can create another data source implementation that is a composition of the local and remote data sources.由于您希望本地数据源充当远程数据源的回退,您可以创建另一个数据源实现,它是本地和远程数据源的组合 This composite data source can contain the fallback logic and handle the delegation to the remote and local datasources as needed.此复合数据源可以包含回退逻辑并根据需要处理对远程和本地数据源的委托 Once you have done this, it is a simple matter to create a Koin module to construct these, and bind the composite data source to the data source interface.完成这些后,创建一个 Koin 模块来构建这些,并将复合数据源绑定到数据源接口是一件简单的事情。

Suppose this is your interface and the two data sources you already have:假设这是您的界面和您已有的两个数据源:

interface DataSource {
    fun getData(): Data
}

class RemoteDataSource : DataSource {
    // ...
}

class LocalDataSource : DataSource {
    // ...
}

Then you can create a third implementation like this one:然后你可以像这样创建第三个实现:

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

To define all of this, your koin module would look something like this要定义所有这些,您的 koin 模块看起来像这样

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

Edit: If what you actually want is a cache, you can use the local data source as your cache like this:编辑:如果你真正想要的是一个缓存,你可以像这样使用本地数据源作为你的缓存:

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()
        }
    }
}

You can try the next approch, it requirs minimal changes and it works for me:您可以尝试下一个方法,它需要最少的更改并且对我有用:

Add interfaces for the remote and local data source, it should inherit the main DataSource interface为远程和本地数据源添加接口,应该继承主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

Then use those interfaces to create koin module然后使用这些接口创建 koin 模块

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM