Jak wybrać odpowiednią architekturę iOS (część 2)

MVC, MVP, MVVM, VIPER lub VIP

Możesz zapoznać się z częścią pierwszą tutaj.

Główne architektury iOS

Krótki przegląd.

MVC

Warstwy MVC są następujące:

M: Logika biznesowa, warstwa sieci i warstwa dostępu do danych

V: Warstwa interfejsu użytkownika (elementy UIKit, scenorysy, Xiby)

C: Koordynuje mediację między modelem a widokiem.

Aby zrozumieć MVC, musimy zrozumieć kontekst, w którym został wymyślony. MVC zostało wynalezione w dawnych czasach tworzenia stron internetowych, w których Views nie ma stanu. W dawnych czasach za każdym razem, gdy potrzebujemy zmiany wizualnej na stronie, przeglądarka ponownie ładuje cały HTML. W tym czasie nie istniała koncepcja utrzymywania i zapisywania stanu widoku.

Byli na przykład niektórzy programiści mieszający się w ramach tego samego pliku HTML, PHP i dostępu do bazy danych. Główną motywacją MVC było oddzielenie warstwy Widok od warstwy Model. Zwiększyło to testowalność warstwy modelu. Podobno w MVC warstwa Widok i Model nie powinna się o sobie nic dowiedzieć. Aby było to możliwe, wynaleziono warstwę pośrednią o nazwie Kontroler. To był zastosowany SRP.

Przykład cyklu MVC:

  1. Wykonywana jest akcja użytkownika / zdarzenie w warstwie widoku (np. Akcja odświeżania) i ta akcja jest przekazywana do kontrolera
  2. Kontroler, który prosi dane do warstwy modelu
  3. Modeluj zwracane dane do kontrolera
  4. Administrator mówi, aby w przypadku widoku zaktualizować jego stan o nowe dane
  5. Zobacz zaktualizuj jego stan

Apple MVC

W iOS kontroler widoku jest sprzężony z UIKit i widokiem cyklu życia, więc nie jest to czysty MVC. Jednak w definicji MVC nie ma nic do powiedzenia, że ​​kontroler nie zna implementacji specyficznej dla widoku lub modelu. Jego głównym celem jest oddzielenie obowiązków warstwy modelu od warstwy widoku, abyśmy mogli jej ponownie użyć i przetestować warstwę modelu w izolacji.

ViewController zawiera widok i jest właścicielem modelu. Problem polega na tym, że pisaliśmy kod kontrolera, a także kod widoku w ViewController.

MVC często tworzy tzw. Problem z kontrolerem masywnego widoku, ale zdarza się to i staje się poważną sprawą w aplikacjach o wystarczającej złożoności.

Istnieje kilka metod, za pomocą których deweloper może usprawnić zarządzanie kontrolerem widoku. Kilka przykładów:

  • Wyodrębnianie logiki VC dla innych klas, takich jak źródło danych metod widoku tabeli i delegowanie dla innych plików przy użyciu wzorca projektu delegowania.
  • Stwórz wyraźniejszy podział obowiązków wraz ze składem (np. Podziel VC na kontrolery widoku potomnego).
  • Użyj wzorca projektowego koordynatora, aby usunąć odpowiedzialność za wdrożenie logiki nawigacji w VC
  • Użyj klasy opakowania DataPresenter, która hermetyzuje logikę i przekształca model danych w dane wyjściowe reprezentujące dane prezentowane użytkownikowi końcowemu.

MVC vs MVP

Jak widać schemat MVP jest bardzo podobny do MVC

MVC było krokiem naprzód, ale nadal było naznaczone brakiem lub milczeniem na temat niektórych rzeczy.

W międzyczasie Internet się rozrósł i ewoluowało wiele rzeczy w społeczności programistów. Na przykład, programiści zaczęli używać Ajax i ładują tylko części stron zamiast całej strony HTML na raz.

W MVC myślę, że nic nie wskazuje na to, że kontroler nie powinien znać konkretnej implementacji widoku (nieobecności).

HTML był częścią warstwy View i wiele przypadków było głupich jak cholera. W niektórych przypadkach odbiera tylko zdarzenia od użytkownika i wyświetla treść wizualną GUI.

Gdy części stron internetowych zaczęły być ładowane na części, segmentacja ta doprowadziła do utrzymania stanu widoku i większej potrzeby oddzielenia odpowiedzialności za logikę prezentacji.

Logika prezentacji to logika, która kontroluje sposób wyświetlania interfejsu użytkownika i interakcji między elementami interfejsu użytkownika. Przykładem jest logika sterowania, kiedy wskaźnik ładowania powinien zacząć pokazywać / animować i kiedy powinien przestać pokazywać / animować.

W MVP i MVVM warstwa widoku powinna być głupia jak cholera bez logiki ani inteligencji, aw iOS kontroler widoku powinien być częścią warstwy widoku. Fakt, że View jest głupi, oznacza, że ​​nawet logika prezentacji pozostaje poza warstwą View.

Jednym z problemów MVC jest to, że nie jest jasne, gdzie powinna pozostać logika prezentacji. Po prostu milczy na ten temat. Czy logika prezentacji powinna znajdować się w warstwie widoku czy w warstwie modelu?

Jeśli rolą Modelu jest po prostu dostarczanie „surowych” danych, oznacza to, że kod w Widoku będzie:

Rozważ następujący przykład: mamy użytkownika o imieniu i nazwisku. W Widoku musimy wyświetlić nazwę użytkownika jako „Nazwisko, Imię” (np. „Flores, Tiago”).

Jeśli rolą Modelu jest dostarczanie „surowych” danych, oznacza to, że kod w Widoku będzie:

let firstName = userModel.getFirstName ()
let lastName = userModel.getLastName ()
nameLabel.text = lastName + „,„ + firstName

Oznacza to, że View zajmie się logiką interfejsu użytkownika. Ale to sprawia, że ​​logika interfejsu użytkownika jest niemożliwa do przetestowania jednostkowego.

Inne podejście polega na tym, że model udostępnia tylko dane, które należy wyświetlić, ukrywając wszelką logikę biznesową w widoku. Ale w końcu otrzymujemy Modele, które obsługują zarówno logikę biznesową, jak i interfejs użytkownika. Byłoby to testowalne jednostkowo, ale wtedy Model kończy się, domyślnie zależny od Widoku.

let name = userModel.getDisplayName ()
nameLabel.text = nazwa

MVP jest tego jasne, a logika prezentacji pozostaje w warstwie prezentacji. Zwiększa to testowalność warstwy Presenter. Teraz model i warstwa prezentacji są łatwe do przetestowania.

Zwykle w implementacjach MVP widok jest ukryty za interfejsem / protokołem i nie powinno być żadnych odniesień do UIKit w Prezenterie.

Inną rzeczą, o której należy pamiętać, są zależności przechodnie.

Jeśli kontroler ma warstwę biznesową jako zależność, a warstwa biznesowa ma warstwę dostępu do danych jako zależność, wówczas kontroler ma zależność przechodnią dla warstwy dostępu do danych. Ponieważ implementacje MVP zwykle używają kontraktu (protokołu) między wszystkimi warstwami, nie ma zależności przechodnich.

Różne warstwy również zmieniają się z różnych powodów i w różnym tempie. Kiedy więc zmieniasz warstwę, nie chcesz, aby powodowało to wtórne efekty / problemy w innych warstwach.

Protokoły są bardziej stabilne niż klasy. Protokoły nie zawierają szczegółów implementacji i umów, więc można zmienić szczegóły implementacji warstwy bez wpływu na inne warstwy.

Tak więc kontrakty (protokoły) tworzą oddzielenie między warstwami.

MVP vs MVVM

Schemat MVVM

Jedną z głównych różnic między MVP i MVVM jest to, że w MVP Prezenter komunikuje się z Widokiem poprzez interfejsy, aw MVVM Widok jest zorientowany na zmiany danych i zdarzeń.

W MVP wykonujemy ręczne wiązanie między Prezenterem a Widokiem za pomocą interfejsów / protokołów.
W MVVM wykonujemy automatyczne wiązanie danych za pomocą czegoś takiego jak RxSwift, KVO lub używamy mechanizmu z lekami generycznymi i zamknięciami.

W MVVM nawet nie potrzebujemy umowy (np .: interfejs Java / protokół iOS) pomiędzy ViewModel i View, ponieważ zwykle komunikujemy się za pośrednictwem Wzorca Projektowego Observer.

MVP używa Wzorca Delegowania, ponieważ Warstwa Prezentująca Deleguje zamówienia do Warstwy Widoku, więc musi wiedzieć coś o Widoku, nawet jeśli jest to tylko podpis interfejsu / protokołu. Pomyśl o różnicy między Centrum powiadomień a delegatami TableView. Centrum powiadomień nie potrzebuje interfejsów do utworzenia kanału komunikacji, ale delegaci TableView korzystają z protokołu, który klasy powinny wdrożyć.

Pomyśl o logice prezentacji wskaźnika ładowania. W MVP prezenter robi ViewProtocol.showLoadingIndicator. W MVVM może istnieć właściwość isLoading w ViewModel. Warstwa Widok poprzez automatyczne powiązanie danych wykrywa zmianę tej właściwości i odświeża się. MVP jest bardziej konieczne niż MVVM, ponieważ Prezenter wydaje rozkazy.

MVVM bardziej dotyczy zmian danych niż bezpośrednich zamówień, a my dokonujemy skojarzeń między zmianami danych i przeglądamy aktualizacje. Jeśli używamy RxSwift i funkcjonalnego paradygmatu programowania reaktywnego wraz z MVVM, sprawiliśmy, że kod jest jeszcze mniej konieczny i bardziej deklaratywny.

MVVM jest łatwiejszy do przetestowania niż MVP, ponieważ MVVM wykorzystuje wzorzec projektowy obserwatora, który przesyła dane między komponentami w sposób niepowiązany.
Możemy więc przetestować po prostu patrząc na zmiany danych, porównując dwa obiekty, zamiast tworzyć fałszywe wywołania metod w celu przetestowania komunikacji między View a Presenter.

PS: Zrobiłem kilka aktualizacji tego artykułu, co spowodowało, że bardzo się rozrósł, więc konieczne było podzielenie go na trzy części. Możesz przeczytać część trzecią tutaj.

Część druga kończy się tutaj. Wszelkie opinie są mile widziane. Część trzecia będzie mówić o VIPER, VIP, programowaniu reaktywnym, kompromisach, ograniczeniach i poczuciu kontekstowym.

Dziękuję za przeczytanie! Jeśli podobał Ci się ten artykuł, proszę klaśnij
aby inni też mogli to przeczytać :)