Sobes.tech
Middle+

Cum ați implementa singur o abordare arhitecturală MVVM?

sobes.tech AI

Răspuns de la AI

MVVM (Model-View-ViewModel) este un model de arhitectură care separă UI, logica de prezentare și logica de afaceri.

Componentele principale:

  • Model: Reprezintă datele și logica de afaceri. Nu depinde de UI.
  • View: Stratul UI. Afișează datele din ViewModel și trimite acțiunile utilizatorului (evenimente) către ViewModel. Nu conține logica de afaceri.
  • ViewModel: Conține logica de prezentare, pregătește datele din Model pentru afișare în View și procesează acțiunile utilizatorului. Nu are legături directe cu View, interacționează prin fluxuri observabile (Observable streams).

Implementare independentă:

  1. Crearea Modelului: Obiecte Java simple POJO sau clase de date Kotlin pentru reprezentarea datelor, depozite pentru accesul la sursele de date (rețea, bază de date).

    // Exemplu Model
    data class User(val id: Int, val name: String, val email: String)
    
    class UserRepository {
        fun getUser(userId: Int): User {
            // Logica de obținere a utilizatorului din sursa de date
            return User(userId, "Test User $userId", "test$userId@example.com")
        }
    }
    
  2. Crearea ViewModel: Clasă care extinde ViewModel din componentele de arhitectură Android (sau o implementare proprie cu conștientizare de ciclul de viață). Păstrează LiveData sau Kotlin StateFlow/SharedFlow pentru date observabile. Conține metode pentru procesarea acțiunilor utilizatorului și actualizarea datelor.

    // Exemplu ViewModel
    class UserViewModel(private val userRepository: UserRepository) : androidx.lifecycle.ViewModel() {
    
        private val _user = MutableLiveData<User>()
        val user: LiveData<User> = _user
    
        fun loadUser(userId: Int) {
            // În aplicația reală - încărcare asincronă
            val loadedUser = userRepository.getUser(userId)
            _user.value = loadedUser // Actualizare LiveData
        }
    
        fun updateUser(newUser: User) {
            // Logica de actualizare a utilizatorului
            _user.value = newUser
        }
    }
    
  3. Implementarea View: Activity sau Fragment. Se leagă de ViewModel, se abonează la datele observabile din ViewModel și actualizează UI-ul la schimbarea datelor. Delegă procesarea evenimentelor utilizatorului (clicuri, introducere text) către ViewModel. Utilizează Data Binding sau View Binding pentru o legare mai declarativă.

    // Exemplu View (Fragment)
    class UserFragment : Fragment() {
    
        private lateinit var viewModel: UserViewModel
        private var _binding: FragmentUserBinding? = null
        private val binding get() = _binding!!
    
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View {
            _binding = FragmentUserBinding.inflate(inflater, container, false)
            return binding.root
        }
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
    
            // Obține ViewModel, preferabil prin ViewModelProvider
            viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
    
            // Abonare la datele observabile
            viewModel.user.observe(viewLifecycleOwner) { user ->
                // Actualizare UI la schimbarea utilizatorului
                binding.userNameTextView.text = user.name
                binding.userEmailTextView.text = user.email
            }
    
            // Delegare eveniment către ViewModel
            binding.loadUserButton.setOnClickListener {
                viewModel.loadUser(1) // Exemplu: încărcare utilizator cu ID 1
            }
        }
    
        override fun onDestroyView() {
            super.onDestroyView()
            _binding = null
        }
    }
    
  4. Legarea componentelor: View creează o instanță a ViewModel (prin ViewModelProvider) și se abonează la datele observabile. ViewModel lucrează cu Model.

Aspecte cheie ale implementării independente:

  • Utilizarea LiveData sau Flow pentru reactivitate și actualizarea automată a UI-ului la schimbarea datelor.
  • Utilizarea ViewModelProvider pentru a asigura ciclul de viață corect al ViewModel.
  • Utilizarea Dependency Injection (de exemplu, Hilt sau Koin) pentru furnizarea dependențelor (de exemplu, UserRepository) în ViewModel.
  • Tratarea operațiunilor asincrone (cereri de rețea, lucru cu baza de date) în ViewModel folosind corutine sau alte mecanisme.

Avantajele acestei abordări:

  • Testabilitate: ViewModel poate fi testat ușor separat de UI.
  • Separarea responsabilităților: Clară separare între UI, logica de prezentare și logica de afaceri.
  • Rezistență la schimbările de configurație: ViewModel se păstrează în timpul rotirii ecranului și altor schimbări de configurație.
  • Îmbunătățirea întreținerii codului.