Cum să alegeți Arhitectura iOS corespunzătoare (partea 2)

MVC, MVP, MVVM, VIPER sau VIP

Puteți consulta prima parte aici.

Principalele arhitecturi iOS

O scurtă privire de ansamblu.

MVC

Straturile MVC sunt următoarele:

M: Logica de afaceri, stratul de rețea și stratul de acces la date

V: UI Layer (lucruri UIKit, Storyboard, Xibs)

C: Coordonează medierea între model și vizualizare.

Pentru a înțelege MVC trebuie să înțelegem contextul în care a fost inventat. MVC a fost inventat în vechile zile de dezvoltare web, în ​​care Views nu are stare. Pe vremuri, de fiecare dată când avem nevoie de o schimbare vizuală pe site-ul web, browserul reîncărcă din nou întregul HTML. La vremea respectivă nu a existat niciun concept despre starea de vizualizare menținută și salvată.

Au fost, de exemplu, unii dezvoltatori care s-au amestecat în același fișier HTML, acces PHP și bază de date. Prin urmare, motivația principală a MVC a fost separarea stratului Vizualizare de stratul Model. Acest lucru a mărit testabilitatea stratului Model. Se presupune că în MVC, stratul Vizualizare și model nu ar trebui să știe nimic despre celălalt. Pentru ca acest lucru să fie posibil, a fost inventat un strat intermediar numit Controller. Acesta a fost SRP care a fost aplicat.

Un exemplu de ciclu MVC:

  1. O acțiune / eveniment al utilizatorului din stratul de vizualizare (de ex .: Acțiune de actualizare) este declanșată și această acțiune este comunicată controlorului
  2. Controlerul care solicită date la nivelul modelului
  3. Modelează datele returnate în controler
  4. Controlorul spune pentru actualizarea Vizualizării stării sale cu noile Date
  5. Vizualizați actualizarea stării sale

Apple MVC

În iOS, View Controller este cuplat la vizualizarea UIKit și ciclul de viață, deci nu este un MVC pur. Cu toate acestea, în definiția MVC, nu există nimic care să spună că controlorul nu poate cunoaște implementarea specifică a modelului View sau Model. Scopul său principal este de a separa responsabilitățile stratului Model de stratul de vizualizare, astfel încât să putem reutiliza și testa stratul Model în mod izolat.

ViewController conține View și deține Modelul. Problema este că am folosit pentru a scrie codul controlerului, precum și codul de vizualizare în ViewController.

MVC creează adesea problema numită Massive View Controller, dar asta se întâmplă doar și devine un lucru serios în aplicațiile cu suficientă complexitate.

Există câteva metode pe care dezvoltatorul le poate utiliza pentru a face controlul de vizualizare mai gestionabil. Cateva exemple:

  • Extragerea logicii VC pentru alte clase, cum ar fi metodele de vizualizare a tabelelor sursă de date și delegarea pentru alte fișiere folosind modelul de design delegat.
  • Creați o separare mai distinctă a responsabilităților cu compoziția (de ex. Divizați VC-ul în controlerele pentru vizualizarea copiilor).
  • Utilizați modelul de proiectare a coordonatorului pentru a elimina responsabilitatea implementării logicii de navigare în VC
  • Utilizați o clasă de înveliș DataPresenter care încapsulează logica și transformă modelul de date într-o ieșire de date reprezentând datele prezentate utilizatorului final.

MVC vs MVP

Cum puteți vedea diagrama MVP este foarte similară cu MVC

MVC a fost un pas înainte, dar totuși a fost marcat de absența sau tăcerea cu privire la unele lucruri.

Între timp, World Wide Web a crescut și multe lucruri din comunitatea dezvoltatorilor au evoluat. De exemplu, programatorii au început să folosească Ajax și să încarce doar părți din pagini în locul întregii pagini HTML simultan.

În MVC cred că nu există nimic care să indice faptul că controlerul nu ar trebui să știe implementarea specifică a View (absență).

HTML a făcut parte din stratul Vizualizare și multe cazuri au fost mutate ca dracu. În unele cazuri, acesta primește doar evenimente de la utilizator și afișează conținutul vizual al GUI.

Pe măsură ce părțile din paginile web au început să fie încărcate în părți, această segmentare a condus în direcția menținerii stării de vizualizare și a unei nevoi mai mari de separare a responsabilității logice de prezentare.

Logica de prezentare este logica care controlează modul în care trebuie afișată interfața de utilizator și modul în care elementele UI interacționează împreună. Un exemplu este logica de control a momentului în care un indicator de încărcare ar trebui să înceapă să afișeze / să animeze și când ar trebui să înceteze afișarea / animarea.

În MVP și MVVM, stratul de vizualizare ar trebui să fie nebun la fel de dracu, fără logică sau inteligență în el, iar în iOS, controlorul de vedere ar trebui să facă parte din stratul de vizualizare. Faptul că View este mută înseamnă că chiar și logica de prezentare rămâne în afara stratului Vizualizare.

Una dintre problemele MVC este că nu este clar unde ar trebui să rămână logica de prezentare. El este pur și simplu tăcut despre asta. Logica de prezentare ar trebui să fie în stratul de vizualizare sau în stratul de model?

Dacă rolul modelului este de a furniza doar datele „brute”, înseamnă că codul din Vizualizare ar fi:

Luați în considerare următorul exemplu: avem un Utilizator, cu nume și prenume. În Vizualizare, trebuie să afișăm numele de utilizator ca „Nume, prenume” (de exemplu, „Flores, Tiago”).

Dacă rolul modelului este de a furniza date „brute”, înseamnă că codul din Vizualizare ar fi:

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

Deci, aceasta înseamnă că ar fi responsabilitatea View-ului de a gestiona logica UI. Dar acest lucru face ca logica UI să fie imposibilă de testat unitar.

Cealaltă abordare este ca Modelul să expună doar datele care trebuie afișate, ascunzând orice logică de business din View. Dar apoi, terminăm cu modele care se ocupă atât de logica de business cât și de UI. Ar fi testabil unitar, dar apoi modelul se termină, implicit fiind dependent de vizualizare.

let name = userModel.getDisplayName ()
numeLabel.text = nume

MVP este clar în acest sens, iar logica de prezentare rămâne în Prezentatorul Strat. Aceasta mărește testabilitatea stratului Prezentator. Acum, Modelul și Prezentatorul Strat sunt testabile cu ușurință.

În mod normal, în implementările MVP, View este ascunsă în spatele unei interfețe / protocol și nu ar trebui să existe referiri la UIKit în Prezentator.

Un alt lucru de reținut este dependențele tranzitorii.

Dacă Controllerul are un nivel de business ca dependență și stratul de afaceri are un nivel de acces la date ca o dependență, atunci controlorul are o dependență tranzitorie pentru stratul de acces la date. Deoarece implementările MVP utilizează în mod normal un contract (protocol) între toate straturile, nu are dependențe tranzitive.

Diferitele straturi se schimbă, de asemenea, din diferite motive și la rate diferite. Deci, atunci când schimbați un strat, nu doriți ca acesta să provoace efecte secundare / probleme în celelalte straturi.

Protocoalele sunt mai stabile decât clasele. Protocoalele nu au detalii de implementare și contracte, astfel încât este posibil să schimbați detaliile de implementare ale unui strat fără a afecta celelalte straturi.

Deci contractele (protocoalele) creează o decuplare între straturi.

MVP vs MVVM

Diagrama MVVM

Una dintre principalele diferențe între MVP și MVVM este că în MVP Prezentatorul comunică cu View prin interfețe, iar în MVVM View este orientat către schimbările de date și evenimente.

În MVP realizăm legarea manuală între Prezentator și View folosind Interfețe / Protocoale.
În MVVM realizăm legarea automată a datelor folosind ceva precum RxSwift, KVO sau folosim un mecanism cu generice și închideri.

În MVVM nici măcar nu avem nevoie de un contract (de exemplu: interfața java / protocolul iOS) între ViewModel și View, deoarece de obicei comunicăm prin modelul de design Observer.

MVP folosește modelul delegat, deoarece delegatorul stratului delegat comandă la stratul de vizualizare, deci trebuie să știe ceva despre vizualizare, chiar dacă este doar semnătura interfaței / protocolului. Gândiți-vă la diferența dintre Centrul de notificări și delegații TableView. Centrul de notificări nu are nevoie de interfețe pentru a crea un canal de comunicare, dar TableView Delegates folosește un protocol pe care ar trebui să îl implementeze clasele.

Gândiți-vă la logica de prezentare a unui indicator de încărcare. În MVP, prezentatorul face ViewProtocol.showLoadingIndicator. În MVVM poate exista o proprietate isLoading în ViewModel. Stratul Vizualizare printr-o legare automată a datelor detectează când această proprietate se schimbă și se reîmprospătează. MVP este mai imperativ decât MVVM, deoarece Prezentatorul dă comenzi.

MVVM se referă mai mult la modificările de date decât la comenzile directe și realizăm asocieri între schimbările de date și actualizările de vizualizare. Dacă folosim RxSwift și paradigma funcțională de programare reactivă împreună cu MVVM, am făcut ca codul să fie și mai puțin imperativ și mai declarativ.

MVVM este mai ușor de testat decât MVP, deoarece MVVM folosește modelul de proiectare a observatorului care transferă datele între componente într-un mod decuplat.
Așadar, putem testa doar analizând modificările datelor doar prin compararea celor două obiecte, mai degrabă decât prin crearea de simulări a metodelor apelurilor pentru a testa comunicarea dintre View și Prezentator.

PS: Am făcut câteva actualizări ale articolului care l-au făcut să crească foarte mult, așa că a fost necesar să îl împărțim în trei părți. Puteți citi a treia parte aici.

Partea a doua se termină aici. Toate feedback-urile sunt binevenite. Partea a treia va vorbi despre VIPER, VIP, programare reactivă, compromisuri, constrângeri și sens contextual.

Vă mulțumim pentru lectură! Dacă v-a plăcut acest articol, vă rugăm să aplaudați
astfel încât și alți oameni să-l poată citi :)