Sådan vælges den relevante iOS-arkitektur (del 2)

MVC, MVP, MVVM, VIPER eller VIP

Du kan konsultere del 1 her.

De vigtigste iOS-arkitekturer

En kort oversigt.

MVC

MVC-lagene er som følger:

M: Forretningslogik, netværkslag og datatilgangslag

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

C: Koordinerer mægling mellem model og visning.

For at forstå MVC må vi forstå den kontekst, hvori den blev opfundet. MVC blev opfundet i de gamle webudviklingsdage, hvor Views ikke har nogen tilstand. I de gamle tider, hver gang vi har brug for en visuel ændring af webstedet, indlæser browseren hele HTML'en igen. På det tidspunkt var der ikke noget begreb om, at synspunktet opretholdes og gemmes.

Der var for eksempel nogle udviklere, der blandede sig i den samme HTML-fil, PHP og databaseadgang. Så hovedmotivationen for MVC var at adskille View-laget fra Model-laget. Dette forøgede testbarheden af ​​modellaget. Visstnok i MVC skulle visningen og modellen ikke vide noget om hinanden. For at dette skulle være muligt, blev der opfundet et mellemliggende lag ved navn Controller. Dette var SRP, der blev anvendt.

Et eksempel på MVC-cyklus:

  1. En brugerhandling / begivenhed i View Layer (f.eks.: Refresh Action) affyres, og denne handling kommunikeres til Controller
  2. Controller, der beder data til Model Layer
  3. Model returnerer data til Controller
  4. Controller siger for View-opdateringen om sin tilstand med de nye data
  5. Se opdater hans tilstand

Apple MVC

I iOS er View Controller koblet til UIKit og livscyklusvisningen, så det er ikke rent MVC. Imidlertid er der i MVC-definitionen intet at sige, at controlleren ikke kan kende den visning eller model-specifikke implementering. Hans hovedformål er at adskille ansvaret fra Model-laget fra View-laget, så vi kan genbruge det og teste Model-laget isoleret.

ViewController indeholder visningen og ejer modellen. Problemet er, at vi plejede at skrive controller-koden samt visningskoden i ViewController.

MVC skaber ofte det kaldte Massive View Controller-problem, men det sker kun og bliver en alvorlig ting i apps med tilstrækkelig kompleksitet.

Der er nogle metoder, som udvikleren kan bruge til at gøre View Controller mere håndterbar. Nogle eksempler:

  • Ekstrahering af VC-logikken for andre klasser, såsom tabelvisningsmetoder, datakilde og delegeret til andre filer ved hjælp af delegatdesignmønsteret.
  • Opret en mere adskilt adskillelse af ansvarsområder med komposition (f.eks. Opdel VC i børnevisningskontrollere).
  • Brug koordinatorens designmønster til at fjerne ansvaret for at implementere navigationslogikken i VC
  • Brug en DataPresenter-indpakningsklasse, der indkapsler logikken og omdanner datamodellen til en dataoutput, der repræsenterer de data, der er præsenteret for slutbrugeren.

MVC vs MVP

Hvordan du kan se diagrammet over MVP, ligner MVC meget

MVC var et skridt fremad, men det var stadig præget af fravær eller stilhed om nogle ting.

I mellemtiden voksede World Wide Web, og mange ting i udviklernes samfund udviklede sig. For eksempel begyndte programmererne at bruge Ajax og indlæser kun dele af sider i stedet for hele HTML-siden på én gang.

I MVC tror jeg, at der ikke er noget, der tyder på, at controller ikke bør kende den specifikke implementering af View (fravær).

HTML var en del af View-laget, og mange sager var dumme som fuck. I nogle tilfælde modtager den kun begivenheder fra brugeren og viser det visuelle indhold i GUI.

Da dele af websiderne begyndte at indlæses i dele, førte denne segmentering i retning af at opretholde View-tilstand og et større behov for en præsentation af logikansvarsadskillelse.

Præsentationslogik er den logik, der styrer, hvordan UI skal vises, og hvordan UI-elementer interagerer sammen. Et eksempel er kontrollogikken for, hvornår en indlæsningsindikator skal begynde at vise / animere, og hvornår den skal stoppe med at vise / animere.

I MVP og MVVM skal View Layer være dumt som fuck uden nogen logik eller intelligens i det, og i iOS skal View Controller være en del af View Layer. At View er dumt betyder, at selv præsentationslogikken forbliver ude af View-laget.

Et af problemerne med MVC er, at det ikke er klart, hvor præsentationslogikken skal forblive. Han er simpelthen tavs om det. Bør præsentationslogikken være i visningslaget eller i modellaget?

Hvis modelens rolle blot er at give "rå" data, betyder det, at koden i visningen er:

Overvej følgende eksempel: vi har en bruger med fornavn og efternavn. I visningen er vi nødt til at vise brugernavnet som "Efternavn, fornavn" (f.eks. "Flores, Tiago").

Hvis modellens rolle er at levere de "rå" data, betyder det, at koden i visningen er:

lad firstName = userModel.getFirstName ()
lad lastName = userModel.getLastName ()
nameLabel.text = efternavn + “,“ + fornavn

Så dette betyder, at det ville være View's ansvar at håndtere UI-logikken. Men dette gør UI-logikken umulig at enhedstest.

Den anden tilgang er at få modellen til kun at eksponere de data, der skal vises, og skjule enhver forretningslogik fra visningen. Men så ender vi med modeller, der håndterer både forretnings- og UI-logik. Det ville være enhedstestbar, men så ender modellen og implicit er afhængig af visningen.

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

MVP er klar over det, og præsentationslogikken forbliver i præsentationslaget. Dette øger testbarheden af ​​Presenter-laget. Nu kan model og præsentationslag let testes.

Normalt i MVP-implementeringerne er visningen skjult bag en interface / protokol, og der skal ikke være nogen henvisninger til UIKit i præsentanten.

En anden ting at huske på er de transitive afhængigheder.

Hvis controlleren har et forretningslag som en afhængighed, og virksomhedslaget har datatilgangslag som afhængighed, har controlleren en transitiv afhængighed for datatilgangslaget. Da MVP-implementeringerne normalt bruger en kontrakt (protokol) mellem alle lag, har den ikke transitive afhængigheder.

De forskellige lag ændres også af forskellige grunde og i forskellige hastigheder. Så når du skifter et lag, ønsker du ikke, at dette medfører sekundære effekter / problemer i de andre lag.

Protokoller er mere stabile end klasser. Protokollerne har ikke implementeringsdetaljer og med kontrakterne, så det er muligt at ændre implementeringsdetaljerne for et lag uden at påvirke de andre lag.

Så kontrakterne (protokoller) skaber en afkobling mellem lagene.

MVP vs MVVM

MVVM diagram

En af de vigtigste forskelle mellem MVP og MVVM er, at i MVP kommunikerer præsentanten med visningen gennem grænseflader, og i MVVM er visningen orienteret til data og begivenhedsændringer.

I MVP laver vi manuel binding mellem Presenter og View ved hjælp af grænseflader / protokoller.
I MVVM laver vi automatisk databinding ved hjælp af noget som RxSwift, KVO eller bruger en mekanisme med generiske og lukninger.

I MVVM har vi endda ikke brug for en kontrakt (f.eks: Java-interface / iOS-protokol) mellem ViewModel og View, fordi vi normalt kommunikerer gennem Observer Design Pattern.

MVP bruger delegerede mønster, fordi præsentationslaget delegerer ordrer til visningslaget, så det er nødvendigt at vide noget om visningen, selvom det kun er interface / protokolsignatur. Tænk på forskellen mellem Notification Center og TableView delegerede. Notifikationscenter har ikke brug for grænseflader for at oprette en kommunikationskanal, men TableView Delegates bruger en protokol, som klasserne skal implementere.

Tænk på præsentationslogikken for en indlæsningsindikator. I MVP gør programlederen ViewProtocol.showLoadingIndicator. I MVVM kan der være en isLoading-egenskab i ViewModel. Vis-laget gennem en automatisk databinding registrerer, når denne egenskab ændres og opdateres. MVP er mere afgørende end MVVM, fordi præsentanten giver ordrer.

MVVM handler mere om dataændringer end direkte ordrer, og vi foretager sammenhænge mellem dataændringer og se opdateringer. Hvis du bruger RxSwift og funktionelt reaktivt programmeringsparadigme sammen med MVVM, har vi gjort koden endnu mindre vigtig og mere erklærende.

MVVM er lettere at teste end MVP, fordi MVVM bruger Observer Design Pattern, der overfører data mellem komponenter på en afkoblet måde.
Så vi kan teste bare ved at se på ændringer i data bare ved at sammenligne de to objekter snarere end at skabe håb om metodernes opkald for at teste kommunikationen mellem View og Presentator.

PS: Jeg lavede nogle opdateringer til artiklen, der fik den til at vokse meget, så det var nødvendigt at opdele den i tre dele. Du kan læse del tre her.

Del to slutter her. Alle feedback er velkomne. Del tre vil tale om VIPER, VIP, reaktiv programmering, trade-offs, begrænsninger og kontekstuel sense.

Tak fordi du læste! Hvis du kunne lide denne artikel, skal du klappe
så andre mennesker kan læse det også :)