我有一个多模块项目,我想在两个不同的模块中将数据和域逻辑彼此分离。 (目前它们都在核心模块中):https://github.com/alirezaeiii/TMDb-Compose-Playground
我的应用程序有一个支持不同屏幕分页的逻辑:
private const val STARTING_PAGE_INDEX = 1
abstract class BasePagingSource<T : TMDbItem>(private val context: Context) : PagingSource<Int, T>() {
protected abstract suspend fun fetchItems(page: Int): List<T>
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, T> {
val page = params.key ?: STARTING_PAGE_INDEX
return try {
val response = fetchItems(page)
LoadResult.Page(
data = response,
prevKey = if (page == STARTING_PAGE_INDEX) null else page - 1,
nextKey = if (response.isEmpty()) null else page + 1
)
} catch (exception: IOException) {
LoadResult.Error(TMDbException(context.getString(R.string.failed_loading_msg)))
} catch (exception: HttpException) {
LoadResult.Error(TMDbException(context.getString(R.string.failed_loading_msg)))
}
}
override fun getRefreshKey(state: PagingState<Int, T>): Int? {
return state.anchorPosition?.let {
state.closestPageToPosition(it)?.prevKey?.plus(1)
?: state.closestPageToPosition(it)?.nextKey?.minus(1)
}
}
}
我有一个基本存储库类:
abstract class BasePagingRepository<T : TMDbItem> {
protected abstract fun pagingSource(query: String?): BasePagingSource<T>
fun fetchResultStream(query: String?= null): Flow<PagingData<T>> = Pager(
config = PagingConfig(pageSize = NETWORK_PAGE_SIZE),
pagingSourceFactory = { pagingSource(query) }
).flow
companion object {
private const val NETWORK_PAGE_SIZE = 20
}
}
这种方法的问题是所有子类都以相同的方式扩展它:
@Singleton
class AiringTodayTvSeriesPagingRepository @Inject constructor(
@ApplicationContext private val context: Context,
private val tvShowApi: TVShowService
) : BasePagingRepository<TVShow>() {
override fun pagingSource(query: String?): BasePagingSource<TVShow> =
AiringTodayTvSeriesPagingSource(context, tvShowApi)
}
或者:
@Singleton
class OnTheAirTvSeriesPagingRepository @Inject constructor(
@ApplicationContext private val context: Context,
private val tvShowApi: TVShowService
) : BasePagingRepository<TVShow>() {
override fun pagingSource(query: String?): BasePagingSource<TVShow> =
OnTheAirTvSeriesPagingSource(context, tvShowApi)
}
核心模块中的 com.sample.tmdb.core.data.repository 包中的等。例如,它们都扩展了
BasePagingRepository<TVShow>
。当然,我已经对存储库使用了依赖注入:
@Singleton
@Binds
internal abstract fun bindTrendingTVShowRepository(trendingTvSeriesPagingRepository: TrendingTvSeriesPagingRepository): BasePagingRepository<TVShow>
@Singleton
@Binds
internal abstract fun bindPopularTVShowRepository(popularTvSeriesPagingRepository: PopularTvSeriesPagingRepository): BasePagingRepository<TVShow>
@Singleton
@Binds
internal abstract fun bindAiringTodayTVShowRepository(airingTodayTvSeriesPagingRepository: AiringTodayTvSeriesPagingRepository): BasePagingRepository<TVShow>
@Singleton
@Binds
internal abstract fun bindOnTheAirTVShowRepository(onTheAirTvSeriesPagingRepository: OnTheAirTvSeriesPagingRepository): BasePagingRepository<TVShow>
因此,当我想将这些存储库注入到我的 ViewModel 中时,我必须使用具体实现而不是抽象或接口(Solid 中的
Dependency inversion
),例如:
@HiltViewModel
class AiringTodayTvSeriesViewModel @Inject constructor(repository: AiringTodayTvSeriesPagingRepository) :
BaseMainPagingViewModel<TVShow>(repository)
因此,第一,我没有遵循依赖关系倒置,第二,我必须将
data
和 domain
模块依赖项添加到我的功能模块中。我认为这不太好,只需添加 domain
依赖项就足够了,因为我的界面和抽象存储库都在那里。
您如何解决这个问题?我很感激任何建议。
我在使用 Hilt 时决定使用限定符注释来实现此目的,例如:
@Retention(AnnotationRetention.BINARY)
@Qualifier
annotation class Discover
在di模块中,我使用它如下:
@Singleton
@Discover
@Binds
internal abstract fun bindDiscoverMoviesRepository(discoverMoviesPagingRepository: DiscoverMoviesPagingRepository): BasePagingRepository<Movie>
我只是在 ViewModel 中使用了注释:
@HiltViewModel
class DiscoverMoviesViewModel @Inject constructor(
@Discover repository: BasePagingRepository<Movie>
) : BaseMainPagingViewModel<Movie>(repository)