Kurs Android (21)

Autor: Damian Chodorek • Opublikowany: 11 września 2016 • Ostatnia aktualizacja: 16 września 2016 • Kategoria: android, kursy

Architektura aplikacji i wzorzec Model-View-Presenter.

Do tej pory, w ramach kursu, poznałeś poszczególne elementy aplikacji w systemie Android. Najbliższe artykuły będą poświęcone ich połączeniu w jedną profesjonalną aplikację. Czym więc jest architektura aplikacji? To sposób w jaki zorganizujesz swój kod oraz zależności, które w nim występują. Dobra architektura sprawi, że Twój kod będzie ustandaryzowany, czytelny, łatwy do przetestowania oraz umożliwiający wprowadzenie nawet poważnych zmian.

Tworząc aplikacje, w systemie Android, możemy korzystać z takich elementów jak np. serwisy, aktywności i fragmenty. Możemy cały kod, odpowiedzialny za daną aktywność, umieścić w jej klasie, np. MainActivity. Wielu początkujących programistów tak robi. To rozwiązanie nie jest najgorsze przy prostych aplikacjach, które niewiele robią, ale przy większych projektach pojawia się kilka problemów.

  • Nasza aktywność staje się god object, czyli obiektem, w którym dzieje się zbyt dużo i występuje wiele zależności. Zamiast uporządkowanego kodu mamy spaghetti, w którym trudno się odnaleźć i które trudno modyfikować.
  • Ponieważ w klasie występuje dużo zależności, trudniej będzie nam wdrożyć testy. Nasz kod będzie przeplatany obiektami androidowymi, dla których trzeba będzie dostarczyć makiety (więcej o testach przeczytasz w przyszłości).
  • Poza specyficznymi zastosowaniami, god object jest uważany za kiepską praktykę. Kod jest po prostu nieprofesjonalny.

Podczas programowania powinniśmy się trzymać reguł SOLID. Jedna z nich (SRP) mówi o tym, że każda klasa powinna realizować wyłącznie jedną funkcjonalność. Jeśli więc aktywność zajmuje się pobieraniem danych, ich edycją oraz wyświetlaniem na ekranie, to złamaliśmy jedną z podstawowych reguł profesjonalnego/czystego kodu.

W najbliższych artykułach poznasz wzorce projektowe, które tak naprawdę są pomysłami na porządną architekturę aplikacji. Trzymanie się tych wzorców sprawi, że Twój kod będzie naprawdę dobry. Dziś poznamy wzorzec MVP (Model-View-Presenter), który jest często wykorzystywaną architekturą podczas tworzenia aplikacji androidowych. Jego znajomość pomoże Ci pisać kod i jest obowiązkowa jeśli chcesz być dobrym programistą Androida.

Wzorzec Model-View-Presenter

Wzorzec MVP jest rozwinięciem MVC. Dzieli on aplikację na 3 elementy: model, view, presenter. Każdy z nich posiada określone zadania. Opis poniżej.

  • Model – to warstwa danych, na których opiera się aplikacja wraz z warstwą, która nimi zarządza i udostępnia. Przykładowo jeśli nasza apka wyświetla listę artykułów, to danymi będą… artykuły, którymi zarządza baza danych oraz które udostępnia przykładowy DbManager.
  • View – to część odpowiedzialna za graficzny interfejs użytkownika. Widok odpowiada za prezentację danych, animacje, komunikowanie błędów. Widok zawiera w sobie klasy androidowe jak RecyclerView, ImageView, Button, TextView itd. Warstwa widoku najczęściej jest implementowana we fragmencie lub aktywności (tu jest mała pułapka, o której napiszę poniżej). Widok powinien być pasywny – to znaczy, że nie powinien decydować o tym jaki stan wyświetlić na ekranie. O tym decyduje prezenter. Aby to było możliwe, widok przekazuje do niego informacje o wszelkich akcjach użytkownika (np. tapnięciach). Na ich podstawie, prezenter zleca widokowi wyświetlenie odpowiednich rzeczy. Widok jest przekaźnikiem interakcji z użytkownikiem i sam na nie nie reaguje.
  • Presenter – tutaj znajduje się główna logika aplikacji. Prezenter odpowiedzialny jest za odbieranie interakcji użytkownika z widoku oraz reagowanie na nie. W ramach reakcji na interakcje, przeważnie pobiera dane (np. z bazy danych – czyli komunikuje się z warstwą modelu), a następnie przekazuje je do widoku i każde wyświetlić. W przypadku, gdy podczas pobierania danych nastąpi jakiś błąd, prezenter zleca widokowi wyświetlenie odpowiedniego komunikatu. W prezenterze znajduje się również walidacja danych, zapis danych, nawigacja po aplikacji – innymi słowy – logika działania. Sam prezenter nie powinien nic wiedzieć o klasach androidowych związanych z GUI. Prezenter pośredniczy pomiędzy widokiem a modelem. Prezenter nie powinien pobierać danych bezpośrednio z bazy danych, a raczej z jakiegoś managera np. DbManagera lub ApiManagera w przypadku danych z sieci.

Opisując warstwę widoku napisałem o małej pułapce. Chodzi o to, że aktywność i fragment to klasy frameworka Android i posiadają one wiele metod związanych z logiką aplikacji, np. rozpoczynanie serwisów, otwieranie innych aktywności, dostęp do SharedPreferences. Należy się więc pilnować, aby w naszej aktywności lub fragmencie nie znalazła się logika, a wyłącznie kod związanych z interfejsem użytkownika (w tym np. zamykanie aktywności). Logika powinna znaleźć się w prezenterze.

Jak w praktyce wygląda działanie MVP?

  1. Użytkownik zrobił na ekranie swipe od góry do dołu, aby odświeżyć listę z danymi, np. artykułami.
  2. Widok wywołuje na prezenterze metodę refreshData().
  3. Prezenter wywołuje na widoku metodę showLoadingIndicator().
  4. Widok wyświetla kręcące się kółeczko, które oznacza, że dane są właśnie pobierane.
  5. Prezenter, korzystając biblioteki Retrofit, pobiera listę danych z web service’u.
  6. Gdy dane przychodzą do prezentera (lista obiektów POJO – Article), ten wywołuje na widoku metodę setData(), w której przekazuje widokowi listę danych. Następnie prezenter wykonuje na widoku metody hideLoadingIndicator() i showData().
  7. Widok ukrywa kręcące się kółeczko i wyświetla świeże dane na ekranie.

Jak widzisz, widok nie podejmuje żadnych decyzji, a jedynie przekazuje żądania do prezentera oraz odbiera polecenia od niego. Widok jest więc pasywny. Całą logikę wykonuje prezenter.

Najczęstsze niejasności

Jak widzisz MVP jest całkiem przyjemnym pomysłem na architekturę aplikacji. Jest to jednak ogólny pomysł, a to powoduje, że pojawia się wiele niejasności. MVP to wzorzec, a nie konkretna implementacja. Możesz go wdrożyć jak Ci się tylko podoba. Poza podstawowymi założeniami, nie ma reguł jak to zrobić. Z drugiej strony powstało wiele dobrych praktyk tworzenia aplikacji opartych o MVP, które są powszechnie stosowane. Postaram się je przytoczyć i rozjaśnić sytuację.

Czy klasy takie jak OnClickListener lub TextWatcher powinny być w warstwie prezentera?

Skoro prezenter odpowiada za logikę, to wydaje się naturalne, że prezenter powinien posiadać wszelkiego rodzaju callbacki. To prawda, ale przyjęło się, że prezenter nie powinien znać tych klas, ponieważ należą one do Adroida, a prezenter powinien być maksymalnie niezależny. Np. jeśli prezenter potrzebuje klasy Context, aby przykładowo uzyskać dostęp do danych, to kontekst powinien być przekazany do niego z warstwy widoku. Następnie prezenter powinien go przekazać dalej lub wykorzystać. Callbackami i listenerami zarządza warstwa widoku.

Czy widokiem zawsze jest aktywność lub fragment?

Oczywiście, że nie. Jak wspomniałem wcześniej – MVP to tylko pomysł, a implementacja zależy od Ciebie. Widok można zaimplementować w osobnej klasie, ale przyjęło się robić to w aktywności lub fragmencie. Czasem nawet ViewGroup może być widokiem oraz posiadać swojego prezentera. Dobrym przykładem takiej sytuacji jest Toolbar, który może posiadać przecież oddzielną logikę.

Czy może być więcej niż jeden prezenter, widok i model?

Oczywiście, że tak. Wyobraź sobie aplikację, która ma aktywność wyświetlającą kilka fragmentów. Pierwszy fragment pokazuje np. listę wiadomości, a drugi listę kontaktów. Fragmenty zmieniamy przy pomocy obiektu ViewPager. Aktywność nazwiemy MainActivity, a fragmenty MessagesFragment oraz ContactsFragment. Aktywność powinna być podzielona na MVP oraz każdy z fragmentów powinien być podzielony na MVP. Aktywność powinna zarządzać zmianą fragmentów. Warstwa widoku będzie odpowiadać za kontener, w którym znajdą się fragmenty, a niezbędna logika znajdzie się w prezenterze MainPresenter.

Fragment MessagesFragment będzie widokiem, który posiada listę RecyclerView oraz listenery. Jego logika (np. kliknięcie w wiadomość) zawarta będzie w osobnym prezenterze MessagesPresenter, który będzie również pobierał dane z bazy danych.

ContactsFragment również będzie widokiem, który posiada swój własny prezenter – ContactsPresenter.

Biblioteka do MVP

Wzorzec architektoniczny Model-View-Presenter możemy zaimplementować sami. Istnieje jednak popularna biblioteka Mosby, która ułatwia implementację MVP. Jest dobrze przemyślana oraz dojrzała. Dodatkowo znacznie ułatwia zmianę orientacji ekranu, która powoduje zniszczenie i odtworzenie aktywności/fragmentu. Wyobraźmy sobie, że podczas zmiany orientacji trwało pobieranie danych z sieci. Ponieważ nasza aktywność została zniszczona i stworzona na nowo, to wszelkie referencje do widoku przestały być aktualne. Istnieje wiele sposobów na rozwiązanie takiego problemu, jednak biblioteka Mosby robi to w sposób naturalny i zgodny z MVP. Zdecydowanie zachęcam do zapoznania się z nią. Warto również dokładniej przejrzeć stronę biblioteki, ponieważ zawiera wiele porad i przykładów zastosowania MVP.

Jeśli nie MVP to co?

Jak wspomniałem na początku – jeśli Twoja aplikacja jest małym projektem, to być może MVP nie będzie aż tak potrzebny, choć nigdy nie wiadomo czy projekt się nie rozwinie. A jeśli tak się stanie, MVP to ułatwi.

MVP jest często stosowany, ale nie jest to jedyny wzorzec architektoniczny. Dzięki niemu nasza aktywność lub fragment nie stanie się god object, ale przy większych projektach, istnieje szansa, że nasz prezenter nim zostanie. Dlatego opiszę również wzorzec VIPER, który jest eleganckim rozszerzeniem MVP i rozdziela odpowiedzialności jeszcze lepiej. Warto go znać i stosować szczególnie jeśli pracujesz przy rozbudowanych projektach.

część 22

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.