【问题标题】:A better understanding for separation of concerns. Android [closed]更好地理解关注点分离。安卓[关闭]
【发布时间】: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 的实现 如您所见,这一项功能并非只有一项工作,实际上,它会根据所处的情况做多项工作。

  1. 它使用 Retrofit 接口获取电影。 -> 究竟应该做什么 然后它要么: 2.1.将电影保存到缓存 或者 2.2.从缓存中获取电影

因此,如果我理解正确的话,如果我们遵循Separation of concerns,它并没有真正做一件事。

这是我认为正确实现的外观:

  1. 在 MainRepository 中,创建 2 个单独的函数。 1 用于将电影保存到缓存,1 用于从缓存中获取电影

  2. 在 ViewModel 中做同样的事情并创建 2 个单独的函数。 1 用于保存到缓存。 1 用于从缓存中获取

  3. 在我们观察的片段中。如果成功 -> 在 ViewModel 中调用 saveCache 函数 如果失败 -> 在 ViewModel 中调用 getCache

请给我一些关于我的思考过程的反馈。我真的很感激!

【问题讨论】:

  • 这类问题与 Stack Overflow 无关,应在代码审查中发布:codereview.stackexchange.com/help/on-topic
  • @Tenfour04 我明白了。好的,我会尝试在那里问。谢谢
  • @SᴀᴍOnᴇᴌᴀ 这就是我所做的。请参阅我发布的链接。
  • @Tenfour04 但如果 OP 没有清楚地阅读该页面并断言代码是主题,即使代码尚未正常工作并发布它,那么它将被关闭为关闭-话题......它经常发生。
  • @SᴀᴍOnᴇᴌᴀ 这是来自应用程序工作示例的一段代码。不用担心。

标签: android kotlin mvvm


【解决方案1】:

存储库模块处理数据操作。这是数据输入和输出,如果您这样想,关注点分离并不真正适合,因为这是两个单独的任务,但是如果您将存储库视为数据和应用程序之间的中介,那么这是一个任务和分离关注是有道理的。

【讨论】:

    猜你喜欢
    • 2013-10-15
    • 2012-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-25
    • 1970-01-01
    相关资源
    最近更新 更多