De juiste iOS-architectuur kiezen (deel 2)

MVC, MVP, MVVM, VIPER of VIP

U kunt deel één hier raadplegen.

De belangrijkste iOS-architecturen

Een kort overzicht.

MVC

De MVC-lagen zijn als volgt:

M: Bedrijfslogica, netwerklaag en laag voor gegevenstoegang

V: UI-laag (UIKit-dingen, Storyboards, Xibs)

C: Coördineert bemiddeling tussen model en weergave.

Om MVC te begrijpen, moeten we de context begrijpen waarin het werd uitgevonden. MVC werd uitgevonden in de oude dagen van webontwikkeling, waar Views geen status heeft. In de oude tijden telkens als we een visuele verandering in de website nodig hebben, laadt de browser de hele HTML opnieuw. Destijds was er nog geen idee dat de beeldstatus werd behouden en opgeslagen.

Er waren bijvoorbeeld enkele ontwikkelaars die binnen hetzelfde HTML-bestand, PHP en dezelfde databasetoegang gebruikten. Dus de belangrijkste motivatie van MVC was om de View-laag te scheiden van de Model-laag. Dit verhoogde de testbaarheid van de modellaag. Verondersteld in MVC, zou de laag View en Model niets van elkaar moeten weten. Om dit mogelijk te maken, is een tussenlaag met de naam Controller uitgevonden. Dit was de SRP die werd toegepast.

Een voorbeeld van de MVC-cyclus:

  1. Een gebruikersactie / gebeurtenis in de View-laag (bijvoorbeeld: Actie vernieuwen) wordt geactiveerd en die actie wordt aan de controller gecommuniceerd
  2. De controller die gegevens naar de modellaag vraagt
  3. Modelleer de retourgegevens naar de controller
  4. Controller zegt voor de View-update zijn status met de nieuwe Data
  5. Bekijk update zijn staat

Apple MVC

In iOS is de View Controller gekoppeld aan de UIKit en de levenscyclusweergave, dus het is geen pure MVC. In de MVC-definitie is er echter niets dat zegt dat de controller de view- of modelspecifieke implementatie niet kent. Zijn belangrijkste doel is om de verantwoordelijkheden van de Modellaag te scheiden van de Beeldlaag, zodat we deze opnieuw kunnen gebruiken en de Modellaag afzonderlijk kunnen testen.

De ViewController bevat de View en bezit het Model. Het probleem is dat we de controllercode en de viewcode in de ViewController hebben geschreven.

MVC creëert vaak het zogenaamde Massive View Controller-probleem, maar dat gebeurt alleen maar en wordt een serieuze zaak in apps met voldoende complexiteit.

Er zijn enkele methoden die de ontwikkelaar kan gebruiken om de View Controller beter beheersbaar te maken. Een paar voorbeelden:

  • Extraheren van de VC-logica voor andere klassen zoals de tabelweergavemethode gegevensbron en delegeren voor andere bestanden met behulp van het ontwerppatroon van de gemachtigde.
  • Creëer een duidelijkere scheiding van verantwoordelijkheden met compositie (bijv. Verdeel de VC in child view controllers).
  • Gebruik het ontwerppatroon van de coördinator om de verantwoordelijkheid voor het implementeren van de navigatielogica in de VC te verwijderen
  • Gebruik een DataPresenter wrapper-klasse die de logica inkapselt en het gegevensmodel omzet in een gegevensuitvoer die de gegevens voorstelt die aan de eindgebruiker worden gepresenteerd.

MVC versus MVP

Hoe u het diagram van MVP kunt zien, lijkt erg op MVC

De MVC was een stap vooruit, maar het werd nog steeds gekenmerkt door afwezigheid of stilte over sommige dingen.

Ondertussen groeide het World Wide Web en veel dingen in de gemeenschap van de ontwikkelaars evolueerden. De programmeurs begonnen bijvoorbeeld Ajax te gebruiken en laadden slechts delen van pagina's in plaats van de hele HTML-pagina tegelijk.

In MVC denk ik dat niets erop wijst dat de controller de specifieke implementatie van View (afwezigheid) niet zou moeten kennen.

HTML was onderdeel van de View-laag en veel gevallen waren dom als neuken. In sommige gevallen ontvangt het alleen gebeurtenissen van de gebruiker en wordt de visuele inhoud van de GUI weergegeven.

Toen delen van de webpagina's in delen begonnen te worden geladen, leidde deze segmentatie in de richting van het handhaven van de weergavestatus en een grotere behoefte aan een scheiding van presentatielogica-verantwoordelijkheid.

Presentatielogica is de logica die bepaalt hoe UI moet worden weergegeven en hoe UI-elementen met elkaar samenwerken. Een voorbeeld is de besturingslogica van wanneer een laadindicator moet beginnen met weergeven / animeren en wanneer deze moet stoppen met weergeven / animeren.

In MVP en MVVM moet de View Layer zo dom als neuken zijn zonder enige logica of intelligentie erin, en in iOS moet de View Controller deel uitmaken van de View Layer. Het feit dat View dom is, betekent dat zelfs de presentatielogica buiten de View-laag blijft.

Een van de problemen van MVC is dat het niet duidelijk is waar de presentatielogica moet blijven. Daar zwijgt hij gewoon over. Moet presentatielogica zich in de weergavelaag of in de modellaag bevinden?

Als het de rol van het model is om alleen de 'onbewerkte' gegevens te verstrekken, betekent dit dat de code in de weergave zou zijn:

Beschouw het volgende voorbeeld: we hebben een gebruiker, met voornaam en achternaam. In de weergave moeten we de gebruikersnaam weergeven als "Achternaam, Voornaam" (bijvoorbeeld "Flores, Tiago").

Als het de rol van het model is om de 'onbewerkte' gegevens te leveren, betekent dit dat de code in de weergave zou zijn:

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

Dit betekent dus dat het de verantwoordelijkheid van de View is om met de UI-logica om te gaan. Maar dit maakt de UI-logica onmogelijk om te testen.

De andere benadering is om het model alleen de gegevens te laten weergeven die moeten worden weergegeven, waarbij alle bedrijfslogica voor de weergave wordt verborgen. Maar dan eindigen we met modellen die zowel bedrijfs- als UI-logica verwerken. Het zou unit-testbaar zijn, maar dan eindigt het Model, impliciet afhankelijk van de View.

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

De MVP is daar duidelijk over en de presentatielogica blijft in de Presenter-laag. Dit verhoogt de testbaarheid van de Presenter-laag. Nu zijn het model en de presentatielaag eenvoudig te testen.

Normaal gesproken is in de MVP-implementaties de weergave verborgen achter een interface / protocol en mogen er geen verwijzingen naar de UIKit in de Presenter zijn.

Een ander ding om in gedachten te houden is de transitieve afhankelijkheden.

Als de controller een bedrijfslaag heeft als afhankelijkheid en de bedrijfslaag heeft een gegevenstoegangslaag als afhankelijkheid, dan heeft de controller een overgangsafhankelijkheid voor de gegevenstoegangslaag. Aangezien de MVP-implementaties normaal gesproken een contract (protocol) tussen alle lagen gebruiken, zijn er geen transitieve afhankelijkheden.

De verschillende lagen veranderen ook om verschillende redenen en met verschillende snelheden. Dus als u een laag verandert, wilt u niet dat dit secundaire effecten / problemen in de andere lagen veroorzaakt.

Protocollen zijn stabieler dan klassen. De protocollen hebben geen implementatiedetails en met de contracten, dus het is mogelijk om de implementatiedetails van een laag te wijzigen zonder de andere lagen te beïnvloeden.

Dus de contracten (protocollen) zorgen voor een ontkoppeling tussen de lagen.

MVP versus MVVM

MVVM-diagram

Een van de belangrijkste verschillen tussen MVP en MVVM is dat in MVP de Presenter communiceert met de View via interfaces en in de MVVM de View is gericht op gegevens- en gebeurtenisveranderingen.

In de MVP maken we handmatige binding tussen Presenter en View met behulp van interfaces / protocollen.
In The MVVM maken we automatische gegevensbinding met behulp van zoiets als RxSwift, KVO of gebruiken we een mechanisme met generieken en sluitingen.

In de MVVM hebben we zelfs geen contract nodig (bijvoorbeeld: Java-interface / iOS-protocol) tussen ViewModel en View, omdat we meestal communiceren via het ontwerppatroon van de waarnemer.

De MVP gebruikt het Delegate Pattern omdat de Presenter-laag orders delegeert naar de View-laag, dus het moet iets over de View weten, zelfs als het alleen de interface / protocolhandtekening is. Denk aan het verschil tussen Kenniscentrum en Afgevaardigden van TableView. Het Berichtencentrum heeft geen interfaces nodig om een ​​communicatiekanaal te maken, maar TableView Delegates gebruikt een protocol dat de klassen moeten implementeren.

Denk aan de presentatielogica van een laadindicator. In MVP doet de presentator ViewProtocol.showLoadingIndicator. In MVVM kan er een eigenschap isLoading zijn in het ViewModel. De View-laag via een automatische gegevensbinding detecteert wanneer deze eigenschap verandert en zichzelf vernieuwt. MVP is noodzakelijker dan MVVM omdat de presentator orders geeft.

MVVM gaat meer over gegevenswijzigingen dan directe bestellingen, en we leggen associaties tussen gegevenswijzigingen en bekijken updates. Als we RxSwift en een functioneel reactief programmeerparadigma gebruiken samen met MVVM, hebben we de code nog minder noodzakelijk en meer declaratief gemaakt.

MVVM is eenvoudiger te testen dan MVP omdat MVVM het Observer-ontwerppatroon gebruikt dat gegevens tussen componenten op ontkoppelde wijze overbrengt.
We kunnen dus testen door alleen naar veranderingen in gegevens te kijken, gewoon door de twee objecten te vergelijken in plaats van het maken van mocks, de methodeaanroepen om de communicatie tussen View en Presenter te testen.

PS: ik heb een aantal updates aan het artikel gedaan waardoor het veel is gegroeid, dus het was noodzakelijk om het in drie delen te splitsen. U kunt deel drie hier lezen.

Deel twee eindigt hier. Alle feedback is welkom. Deel drie gaat over VIPER, VIP, Reactive Programming, Trade-Offs, Constraints en Contextual Sense.

Bedankt voor het lezen! Als je dit artikel leuk vond, klap dan
zodat andere mensen het ook kunnen lezen :)