Hvordan velge iOS-arkitektur (del 2)

MVC, MVP, MVVM, VIPER eller VIP

Du kan konsultere del 1 her.

De viktigste iOS-arkitekturene

En kort oversikt.

MVC

MVC-lagene er som følger:

M: Business Logic, Network Layer and Data Access Layer

V: UI Layer (UIKit ting, Storyboards, Xibs)

C: Koordinerer mekling mellom Model og View.

For å forstå MVC må vi forstå konteksten den ble oppfunnet i. MVC ble oppfunnet i de gamle webutviklingsdagene, der Views ikke har noen tilstand. I gamle tider hver gang vi trenger en visuell endring på nettstedet, laster nettleseren på nytt hele HTML-en igjen. På det tidspunktet var det ingen begrep om at utsiktsstaten ble opprettholdt og frelst.

Det var for eksempel noen utviklere som blandet seg i den samme HTML-filen, PHP og databasetilgang. Så hovedmotivasjonen til MVC var å skille View-laget fra Model-laget. Dette økte testbarheten til modellsjiktet. Visstnok i MVC, bør View and Model-laget ikke vite noe om hverandre. For at dette skulle være mulig, ble det oppfunnet et mellomliggende lag med navnet Controller. Dette var SRP som ble brukt.

Et eksempel på MVC-syklusen:

  1. En brukerhandling / hendelse i View Layer (f.eks: Refresh Action) blir avfyrt og den handlingen blir kommunisert til Controller
  2. Kontrolleren som ber data til Model Layer
  3. Modell returnerer dataene til kontrolleren
  4. Kontrollør sier for View-oppdateringen sin tilstand med de nye dataene
  5. Se oppdatere hans tilstand

Apple MVC

I iOS er View Controller koblet til UIKit og livssyklusvisningen, så det er ikke ren MVC. I MVC-definisjonen er det imidlertid ingenting som sier at kontrolleren ikke kan kjenne den visningen eller modellspesifikk implementering. Hovedformålet hans er å skille ansvaret fra Model-laget fra View-laget, slik at vi kan bruke det på nytt og teste Model-laget isolert.

ViewController inneholder visningen og eier modellen. Problemet er at vi pleide å skrive kontrollerkoden samt visningskoden i ViewController.

MVC skaper ofte det kalte Massive View Controller-problemet, men det skjer bare og blir en alvorlig ting i apper med nok kompleksitet.

Det er noen metoder som utvikleren kan bruke for å gjøre View Controller mer håndterbar. Noen eksempler:

  • Å trekke ut VC-logikken for andre klasser, som tabellvisningsmetoden datakilde og delegere for andre filer ved hjelp av delegatdesignmønsteret.
  • Lag en mer tydelig adskillelse av ansvarsforhold med komposisjon (f.eks. Del VC-en i kontrollere av barnevisninger).
  • Bruk koordinatorens designmønster for å fjerne ansvaret for å implementere navigasjonslogikken i VC
  • Bruk en DataPresenter-innpakningsklasse som innkapsler logikken og transformerer datamodellen til en datautgang som representerer dataene som er presentert for sluttbrukeren.

MVC vs MVP

Hvordan du kan se diagrammet for MVP, ligner veldig på MVC

MVC var et skritt fremover, men det var fortsatt preget av fravær eller stillhet rundt noen ting.

I mellomtiden vokste World Wide Web og mange ting i utviklermiljøet utviklet seg. For eksempel begynte programmererne å bruke Ajax og laste bare deler av sider i stedet for hele HTML-siden på en gang.

I MVC tror jeg det ikke er noe som tyder på at kontrolleren ikke burde vite den konkrete implementeringen av View (fravær).

HTML var en del av View-laget, og mange tilfeller var stumme som faen. I noen tilfeller mottar den bare hendelser fra brukeren og viser det visuelle innholdet i GUI.

Etter hvert som deler av websidene begynte å bli lastet inn i deler, førte denne segmenteringen i retning av å opprettholde View-tilstand og et større behov for en separasjon av ansvarlig presentasjonslogikk.

Presentasjonslogikk er logikken som styrer hvordan brukergrensesnitt skal vises og hvordan brukergrensesnittelementer samvirker sammen. Et eksempel er kontrolllogikken for når en lasteindikator skal begynne å vise / animere og når den skal slutte å vise / animere.

I MVP og MVVM skal View Layer være stum som faen uten noen logikk eller intelligens i det, og i iOS bør View Controller være en del av View Layer. At View er stum, betyr at selv presentasjonslogikken holder seg utenfor View-laget.

Et av problemene med MVC er at det ikke er klart hvor presentasjonslogikken skal holde seg. Han er rett og slett taus om det. Bør presentasjonslogikken være i View-laget eller i Model Layer?

Hvis modellens rolle er å bare gi "rå" data, betyr det at koden i visningen vil være:

Tenk på følgende eksempel: vi har en bruker, med fornavn og etternavn. I visningen må vi vise brukernavnet som "Etternavn, fornavn" (f.eks. "Flores, Tiago").

Hvis modellens rolle er å gi "rå" data, betyr det at koden i visningen vil være:

la firstName = userModel.getFirstName ()
la lastName = userModel.getLastName ()
nameLabel.text = etternavn + “,“ + fornavn

Så dette betyr at det ville være Views ansvar å håndtere UI-logikken. Men dette gjør UI-logikken umulig å enhetstest.

Den andre tilnærmingen er å la modellen bare eksponere dataene som må vises, og skjule enhver forretningslogikk fra visningen. Men så ender vi opp med modeller som håndterer både forretnings- og brukergrensesnitt-logikk. Det kan testes på enheten, men da ender modellen opp, implisitt avhengig av utsikten.

la navn = userModel.getDisplayName ()
nameLabel.text = navn

MVP er tydelig på det, og presentasjonslogikken forblir i Presentasjonslaget. Dette øker testbarheten til Presenter-laget. Nå er Model and Presenter Layer enkelt testbar.

Normalt i MVP-implementeringene er View skjult bak et grensesnitt / protokoll, og det skal ikke være noen referanser til UIKit i Presentatøren.

En annen ting å huske på er de transitive avhengighetene.

Hvis kontrolleren har et virksomhetslag som en avhengighet og virksomhetslaget har datatilgangslag som en avhengighet, har kontrolleren en transitiv avhengighet for datatilgangslaget. Siden MVP-implementeringene vanligvis bruker en kontrakt (protokoll) mellom alle lag, har den ikke transitive avhengigheter.

De forskjellige lagene endres også av forskjellige grunner og i forskjellige hastigheter. Så når du bytter et lag, vil du ikke at dette skal forårsake sekundære effekter / problemer i de andre lagene.

Protokoller er mer stabile enn klasser. Protokollene har ikke implementeringsdetaljer og med kontraktene, så det er mulig å endre implementeringsdetaljene til et lag uten å påvirke de andre lagene.

Så kontraktene (protokollene) skaper en avkobling mellom lagene.

MVP vs MVVM

MVVM diagram

En av hovedforskjellene mellom MVP og MVVM er at i MVP kommuniserer Presentatøren med visningen gjennom grensesnitt, og i MVVM er visningen orientert mot data og hendelsesendringer.

I MVP lager vi manuell binding mellom Presentator og View ved å bruke grensesnitt / protokoller.
I MVVM lager vi automatisk datainbinding ved hjelp av noe som RxSwift, KVO eller bruker en mekanisme med generikk og stenginger.

I MVVM trenger vi ikke en kontrakt (f.eks: Java-grensesnitt / iOS-protokoll) mellom ViewModel og View fordi vi vanligvis kommuniserer gjennom Observer Design Pattern.

MVP bruker delegerte mønster fordi presentasjonslaget delegerer ordrer til visningslaget, så det trenger å vite noe om visningen, selv om det bare er grensesnitt / protokollsignatur. Tenk på forskjellen mellom varslingssenter og TableView-delegater. Varslingssenteret trenger ikke grensesnitt for å opprette en kommunikasjonskanal, men TableView Delegates bruker en protokoll som klassene skal implementere.

Tenk på presentasjonslogikken til en lasteindikator. I MVP gjør programlederen ViewProtocol.showLoadingIndicator. I MVVM kan det være en isLoading-egenskap i ViewModel. Visningslaget gjennom en automatisk databinding oppdager når denne egenskapen endres og oppdateres. MVP er mer avgjørende enn MVVM fordi Presentatøren gir ordre.

MVVM handler mer om dataendringer enn direkte bestillinger, og vi lager assosiasjoner mellom dataendringer og se oppdateringer. Hvis du bruker RxSwift og funksjonelt reaktivt programmeringsparadigme sammen med MVVM, har vi gjort koden enda mindre avgjørende og mer erklærende.

MVVM er enklere å teste enn MVP fordi MVVM bruker Observer Design Pattern som overfører data mellom komponenter på en avkoblet måte.
Så vi kan teste bare ved å se på endringer i data bare ved å sammenligne de to objektene i stedet for å lage håne metodekallene for å teste kommunikasjonen mellom View og Presentator.

PS: Jeg gjorde noen oppdateringer til artikkelen som fikk den til å vokse mye, så det var nødvendig å dele den opp i tre deler. Du kan lese del tre her.

Del to slutter her. Alle tilbakemeldinger er velkomne. Del tre vil snakke om VIPER, VIP, Reaktiv programmering, trade-offs, begrensninger og kontekstuell sense.

Takk for at du leser! Hvis du likte denne artikkelen, kan du klappe
slik at andre mennesker kan lese den også :)