Sobes.tech
Middle+

Jak samodzielnie zrealizowałbyś podejście architektoniczne MVVM?

sobes.tech AI

Odpowiedź od AI

MVVM (Model-View-ViewModel) to wzorzec architektoniczny, który oddziela interfejs użytkownika, logikę prezentacji i logikę biznesową.

Główne komponenty:

  • Model: Reprezentuje dane i logikę biznesową. Nie zależy od UI.
  • View: Warstwa UI. Wyświetla dane z ViewModel i wysyła akcje użytkownika (zdarzenia) do ViewModel. Nie zawiera logiki biznesowej.
  • ViewModel: Zawiera logikę prezentacji, przygotowuje dane z Modelu do wyświetlenia w View i obsługuje akcje użytkownika. Nie ma bezpośrednich odniesień do View, komunikuje się przez obserwowalne dane (Observable streams).

Samodzielna implementacja:

  1. Tworzenie Modelu: Proste obiekty POJO lub klasy danych Kotlin do reprezentacji danych, repozytoria do dostępu do źródeł danych (sieć, baza danych).

    // Przykład Modelu
    data class User(val id: Int, val name: String, val email: String)
    
    class UserRepository {
        fun getUser(userId: Int): User {
            // Logika pobierania użytkownika z źródła danych
            return User(userId, "Testowy użytkownik $userId", "test$userId@example.com")
        }
    }
    
  2. Tworzenie ViewModel: Klasa dziedzicząca z ViewModel z komponentów architektury Android (lub własna implementacja z obsługą cyklu życia). Przechowuje LiveData lub Kotlin StateFlow/SharedFlow dla danych obserwowalnych. Zawiera metody obsługi akcji użytkownika i aktualizacji danych.

    // Przykład ViewModel
    class UserViewModel(private val userRepository: UserRepository) : androidx.lifecycle.ViewModel() {
    
        private val _user = MutableLiveData<User>()
        val user: LiveData<User> = _user
    
        fun loadUser(userId: Int) {
            // W rzeczywistej aplikacji - asynchroniczne ładowanie
            val loadedUser = userRepository.getUser(userId)
            _user.value = loadedUser // Aktualizacja LiveData
        }
    
        fun updateUser(newUser: User) {
            // Logika aktualizacji użytkownika
            _user.value = newUser
        }
    }
    
  3. Implementacja widoku: Activity lub Fragment. Łączy się z ViewModel, subskrybuje obserwowalne dane z ViewModel, aktualizuje UI przy zmianie danych. Deleguje obsługę zdarzeń użytkownika (kliknięcia, wpisy tekstu) do ViewModel. Używa Data Binding lub View Binding dla bardziej deklaratywnego powiązania.

    // Przykład widoku (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)
    
            // Uzyskanie ViewModel, najlepiej przez ViewModelProvider
            viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
    
            // Subskrypcja na obserwowalne dane
            viewModel.user.observe(viewLifecycleOwner) { user ->
                // Aktualizacja UI przy zmianie użytkownika
                binding.userNameTextView.text = user.name
                binding.userEmailTextView.text = user.email
            }
    
            // Delegacja zdarzenia do ViewModel
            binding.loadUserButton.setOnClickListener {
                viewModel.loadUser(1) // Przykład: ładowanie użytkownika o ID 1
            }
        }
    
        override fun onDestroyView() {
            super.onDestroyView()
            _binding = null
        }
    }
    
  4. Powiązanie komponentów: View tworzy instancję ViewModel (przez ViewModelProvider) i subskrybuje jej obserwowalne dane. ViewModel pracuje z Modelem.

Kluczowe aspekty samodzielnej implementacji:

  • Użycie LiveData lub Flow do reaktywności i automatycznej aktualizacji UI przy zmianie danych.
  • Użycie ViewModelProvider do zapewnienia poprawnego cyklu życia ViewModel.
  • Użycie Dependency Injection (np. Hilt lub Koin) do dostarczenia zależności (np. UserRepository) do ViewModel.
  • Obsługa operacji asynchronicznych (zapytania sieciowe, praca z bazą danych) w ViewModel z użyciem korutyn lub innych mechanizmów.

Zalety takiego podejścia:

  • Testowalność: ViewModel można łatwo przetestować niezależnie od UI.
  • Podział odpowiedzialności: Jasny podział między UI, logiką prezentacji i logiką biznesową.
  • Odporność na zmiany konfiguracji: ViewModel jest zachowany podczas obracania ekranu i innych zmian konfiguracji.
  • Lepsza utrzymanie kodu.