【发布时间】:2020-10-06 16:41:47
【问题描述】:
我是一名只有 5 个月经验的 Android 开发人员。我仍在学习并努力做到最好。
现在我对Separation of concerns 的概念很感兴趣。我确实理解它的含义,并且我正在尽我最大的努力在我的 android 开发中遵循它,但是,当我开始学习 Clean Architecture 时,我发现自己有点困惑。
我想展示我的一小段代码,如果可能的话,在Separation of concerns 时获得一些关于我的实现及其正确性的反馈
代码[注意:我在这个应用程序中使用 MVVM]:
PopularMoviesFragment:
class MoviesPopularFragment : Fragment() {
private val moviesViewModel: MoviesViewModel by activityViewModels()
private val adapter = PopularMoviesRecyclerAdapter()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding = FragmentMoviesPopularBinding.inflate(inflater, container, false)
binding.viewModel = moviesViewModel
binding.popularMoviesRV.layoutManager = GridLayoutManager(requireContext(), 2)
binding.popularMoviesRV.adapter = adapter
moviesViewModel.popularMoviesState.observe(viewLifecycleOwner, { state ->
when (state) {
is PopularMoviesState.Content -> {
// TODO: Find a way to deal with this via DataBinding
binding.popularMoviesSwipeRefresh.isRefreshing = false
binding.progressBarLayout.visibility = View.GONE
binding.pageNavigation.visibility = View.VISIBLE
adapter.submitList(state.response.results)
}
is PopularMoviesState.Failure -> {
// TODO: Find a way to deal with this via DataBinding
binding.popularMoviesSwipeRefresh.isRefreshing = false
binding.progressBarLayout.visibility = View.GONE
binding.pageNavigation.visibility = View.VISIBLE
adapter.submitList(state.cachedListOfMovies)
showSnackbar(requireView(), getString(R.string.movies_popular_message_viewing_cached_content), Snackbar.LENGTH_INDEFINITE)
}
}
})
// TODO: Look into implementing this via DataBinding
binding.popularMoviesSwipeRefresh.setOnRefreshListener {
binding.popularMoviesSwipeRefresh.isRefreshing = true
moviesViewModel.fetchPopularMovies()
}
return binding.root
}
private fun showSnackbar(view: View, message: String, length: Int) {
val snackbar = Snackbar.make(view, message, length)
snackbar.animationMode = Snackbar.ANIMATION_MODE_SLIDE
snackbar.setAction(view.context.getString(R.string.alert_dialog_default_button_ok)) { snackbar.dismiss() }
snackbar.show()
}
}
MoviesViewModel.kt:
fun fetchPopularMovies(page: Int = currentPage) {
viewModelScope.launch {
val result = MainRepository.fetchPopularMovies(page = page)
_popularMoviesState.value = when (result) {
is PopularMoviesState.Loading -> PopularMoviesState.Loading
is PopularMoviesState.Content -> {
//TODO: Perhaps find a way to work with pages in a better, more isolated way.
currentPage = result.response.page
PopularMoviesState.Content(result.response)
}
is PopularMoviesState.Failure -> {
PopularMoviesState.Failure(result.errorMessage, result.cachedListOfMovies)
}
}
}
}
MainRepository.kt
suspend fun fetchPopularMovies(page: Int = 1) = try {
val response = IPopularMovies.getInstance().getMovies(page = page)
if (response.code() == 200) {
savePopularMoviesToCache(response.body()!!.results)
PopularMoviesState.Content(response.body()!!)
} else {
PopularMoviesState.Failure(response.body()!!.status_message, popularMovieDao.getMovies() ?: listOf())
}
} catch (error: Throwable) {
PopularMoviesState.Failure(error.localizedMessage!!, popularMovieDao.getMovies() ?: listOf())
}
private suspend fun savePopularMoviesToCache(listOfMovies: List<PopularMovie>) {
popularMovieDao.clearTable()
popularMovieDao.addMovies(listOfMovies)
}
我最感兴趣的是MainRepository 及其对fetchPopularMovies 的实现
如您所见,这一项功能并非只有一项工作,实际上,它会根据所处的情况做多项工作。
- 它使用 Retrofit 接口获取电影。 -> 究竟应该做什么 然后它要么: 2.1.将电影保存到缓存 或者 2.2.从缓存中获取电影
因此,如果我理解正确的话,如果我们遵循Separation of concerns,它并没有真正做一件事。
这是我认为正确实现的外观:
-
在 MainRepository 中,创建 2 个单独的函数。 1 用于将电影保存到缓存,1 用于从缓存中获取电影
-
在 ViewModel 中做同样的事情并创建 2 个单独的函数。 1 用于保存到缓存。 1 用于从缓存中获取
-
在我们观察的片段中。如果成功 -> 在 ViewModel 中调用 saveCache 函数 如果失败 -> 在 ViewModel 中调用 getCache
请给我一些关于我的思考过程的反馈。我真的很感激!
【问题讨论】:
-
这类问题与 Stack Overflow 无关,应在代码审查中发布:codereview.stackexchange.com/help/on-topic
-
@Tenfour04 我明白了。好的,我会尝试在那里问。谢谢
-
@SᴀᴍOnᴇᴌᴀ 这就是我所做的。请参阅我发布的链接。
-
@Tenfour04 但如果 OP 没有清楚地阅读该页面并断言代码是主题,即使代码尚未正常工作并发布它,那么它将被关闭为关闭-话题......它经常发生。
-
@SᴀᴍOnᴇᴌᴀ 这是来自应用程序工作示例的一段代码。不用担心。