Назад к задачам
Middle — Senior
8

Улучшение структуры кода: вариант 1

Получайте помощь с лайвкодингом в реальном времени с Sobes Copilot
Условие задачи

Необходимо провести рефакторинг представленного кода, повысив его читаемость, тестируемость и отделив бизнес‑логику от UI‑слоя. При этом следует обратить внимание на корректное использование корутин, Flow и Rx‑компонентов, а также на правильное управление ресурсами в ViewModel.

class ScreenViewModel(
    val configs: ConfigRepository,
    val holder: UserHolder,
    val cards: CardRepository,
    val analytics: AnalyticsService,
    val context: Context
) : ViewModel() {

    lateinit var config: Config
    lateinit var card: CardType

    var liveData = MutableLiveData<UiModel>()
    var successfulChecks = 0L

    init {
        GlobalScope.launch {
            flowOf(configs.loadConfig())
                .zip(cards.observeAvailableCard(), { it1, it2 -> Pair(it1, it2) })
                .flowOn(Dispatchers.Default)
                .filter {
                    it.second == CardType.PREMIUM && holder.getUserScore() > 42 ||
                    it.second != CardType.PREMIUM &&
                    it.first.specialUsers.contains(holder.getUserId())
                }
                .onEach {
                    config = it.first
                    card = it.second
                }
                .flatMapConcat {
                    flowOf(configs.checkBlackList(it.second, holder.getUserId()))
                        .apply { successfulChecks++ }
                }
                .collect {
                    liveData.value = UiModel(
                        title = when {
                            card == CardType.CREDIT -> config.creditTitle
                            card == CardType.DEBIT -> config.debitTitle
                            card == CardType.KID -> config.kidTitle
                            card == CardType.PREMIUM -> context.getString(R.string.congratulations)
                            else -> ""
                        },
                        description = context.getString(
                            R.string.description_pattern,
                            config.kinderSurprizeCashback
                        )
                    )
                }
        }
    }

    var disposables: CompositeDisposable? = CompositeDisposable()

    override fun onCleared() {
        GlobalScope.launch {
            analytics.sendSuccessfulChecks(successfulChecks)
        }
        super.onCleared()
    }
}

interface Config {
    val debitTitle: String
    val creditTitle: String
    val kidTitle: String
    val kinderSurprizeCashback: Long
    val specialUsers: List<Long>
}

interface ConfigRepository {
    suspend fun loadConfig(): Config

    fun checkBlackList(cardType: CardType, userId: Long): BlackList
}

interface BlackList {
    var successfulChecks: Long
}

interface CardRepository {
    fun observeAvailableCard(): Flow<CardType>
}

interface UserHolder {
    fun getUserId(): Long
    fun getUserScore(): Long
}

interface AnalyticsService {
    @FormUrlEncoded
    @POST("/analytics")
    suspend fun sendSuccessfulChecks(count: Long)
}

enum class CardType { DEBIT, CREDIT, KID, PREMIUM }

class UiModel(val title: String, val description: String)