Waarom MVC misschien niet het beste patroon is voor cacao-apps

MVC betekent Model-View-Controller, en het is een wijd verbreid architecturaal patroon voor softwareontwikkeling. Het is het de facto ontwerppatroon voor de ontwikkeling van cacao, en dat is al vele jaren het geval. De meesten van ons kunnen ons niet voorstellen applicaties zonder te bouwen. Zowel UIKit (iOS) als AppKit(macOS) maken veelvuldig gebruik van MVC. Het lijkt bijna alsof we geen andere optie hebben om applicaties voor iOS, tvOS, macOS en watchOS te bouwen.

Zelfs als je niet bekend bent met het Model-View-Controller-patroon, als je de ambitie hebt om applicaties voor een van Apple's platforms te ontwikkelen, moet je leren hoe views (iOS) en windows (macOS) zich verhouden tot controllers en welke rol de model speelt in een typische Cocoa-toepassing. Gelukkig is MVC gemakkelijk te leren.

In deze korte serie leg ik uit wat MVC is, hoe het eruit ziet in een typische Cocoa-applicatie, en waarom het misschien niet de beste oplossing is voor Cocoa-ontwikkelaars..

1. Een voorbeeld

Laat me je laten zien hoe het MVC-patroon eruit ziet in een typische Cocoa-applicatie. Het voorbeeld dat ik je zal laten zien is gericht op iOS, maar alles wat we bespreken is ook van toepassing op tvOS, macOS en watchOS. Open Xcode en maak een nieuwe iOS project gebaseerd op de Toepassing enkele weergave sjabloon.

Geef het project een naam MVC, En instellen Taal naar Snel en apparaten naar iPhone. Ik gebruik Xcode 8 voor deze zelfstudie. De configuratieopties van het project zien er misschien iets anders uit als u Xcode 9 gebruikt.

Zoals de naam al aangeeft, definieert het patroon Model-View-Controller drie componenten, de model-, de uitzicht, en de controleur. Laat me je laten zien waar je deze componenten kunt vinden in een typisch iOS-project.

Controllers

De controllers van een iOS-applicatie zijn view controllers, exemplaren van de UIViewController klasse of een subklasse daarvan. De UIViewController klasse is gedefinieerd in de UIKit kader. Omdat we de Toepassing enkele weergave sjabloon bij het opzetten van het project heeft Xcode voor ons een controller gemaakt, de ViewController klasse, gedefinieerd in ViewController.Swift. Het erft van de UIViewController klasse.

Zoals de naam aangeeft, UIViewController instantie is verantwoordelijk voor het beheren van een weergave, een instantie van de UIView klasse. Elke weergavecontroller in een iOS-project houdt een sterke verwijzing naar een weergave, een ander onderdeel van het patroon Model-View-Controller. De UIView klasse is ook gedefinieerd in de UIKit kader.

Keer bekeken

We kunnen de weergavecomponent vinden in het hoofdverhaallboard van het project. Open Main.storyboard in de Project Navigator aan de linkerkant en inspecteer de Bekijk Controller Scene. De scène bevat een view-controller, een instantie van de ViewController klasse, en het beheert een UIView aanleg.

kiezen Uitzicht in het storyboard aan de linkerkant en open de Identiteitsinspecteur aan de rechterkant. De Klasse gezichtsveld is ingesteld op UIView. In een iOS-applicatie zijn weergaven meestal voorbeelden van UIKit's UIView klasse of een subklasse daarvan.

modellen

Tot nu toe hebben we de controllerlaag en de viewlaag onderzocht. Maar waar kunnen we de modellaag van het project vinden? Het model is bijna altijd specifiek voor het project waaraan u werkt en het is aan u om het model van het project te definiëren, te implementeren en te gebruiken. ik schrijf model-, maar je hebt meestal meerdere modellen, afhankelijk van de complexiteit van je project.

Laten we het laatste stukje van de MVC-puzzel toevoegen door een model te maken. Maak een nieuw Swift-bestand en noem het Person.swift.

kiezen Person.swift in de Project Navigator aan de linkerkant en definieer een structuur met de naam Persoon. We definiëren drie eigenschappen: 

  • Voornaam van type Draad
  • achternaam van type Draad
  • leeftijd van type Int
struct Persoon let firstName: String let lastName: String let age: Int

U hebt nu een model dat u in uw project kunt gebruiken. Laten we het eenvoudig houden en een eigenschap definiëren, persoon, van type Persoon? in de ViewController klasse. We creëren een Persoon bijvoorbeeld in de view controller's viewDidLoad () methode en wijs het toe aan de persoon eigendom.

import UIKit class ViewController: UIViewController // MARK: - Eigenschappen var persoon: Persoon? // MARK: - View Life Cycle-onderdrukking func viewDidLoad () super.viewDidLoad () // Create Person person = Person (firstName: "John", lastName: "Doe", age: 40)

Wat we in dit voorbeeld zien, is heel gebruikelijk in Cocoa-toepassingen die worden aangedreven door het patroon Model-View-Controller. De view-controller bezit en beheert het model en gebruikt het model om de weergave te vullen. In een complexere toepassing laadt u de gegevens van het model vanuit een permanente winkel of haalt u deze op van een externe back-end.

Laten we een uitlaat definiëren voor een UILabel bijvoorbeeld in de view controller en, in het hoofd storyboard, een label toevoegen aan de Bekijk Controller Scene.

import UIKit class ViewController: UIViewController // MARK: - Properties @IBOutlet var label: UILabel! ...

In de view controller's viewDidLoad () methode, we pakken veilig de waarde uit die is opgeslagen in de persoon eigenschap en gebruik de gegevens om de tekst eigendom van de UILabel aanleg.

override func viewDidLoad () super.viewDidLoad () // Create Person person = Person (firstName: "John", lastName: "Doe", age: 40) // Label vullen als let person = person label.text = " \ (person.lastName), \ (person.firstName) (\ (person.age)) "

Het resultaat is niet erg verrassend als je bekend bent met de ontwikkeling van Cocoa. Dit is waar we uiteindelijk mee eindigen.

2. Wat is Model-View-Controller?

Het Model-View-Controller patroon is gemakkelijk te begrijpen en op te pikken. Ondanks de eenvoud kunt u een breed scala aan smaken van het MVC-patroon vinden. MVC biedt alleen een basisblauwdruk die kan worden aangepast aan het platform waarop het wordt gebruikt. 

Het Model-View-Controller-patroon dat u kent op iOS, tvOS, macOS en watchOS verschilt op subtiele manieren van de oorspronkelijke definitie. Hoewel de verschillen in vergelijking met de oorspronkelijke definitie subtiel zijn, hebben ze een aanzienlijke invloed op de code die u schrijft en op de onderhoudbaarheid van het project.

Koetjes en kalfjes

Het Model-View-Controller-patroon is een oud ontwerppatroon. Het maakte zijn eerste verschijning in de jaren 1970 in Smalltalk. Het patroon is bedacht door Trygve Reenskaug. In de loop der jaren heeft het Model-View-Controller-patroon zijn weg gevonden in vele talen en kaders, waaronder Java, Rails en Django.

Ik noemde eerder dat het MVC-patroon applicaties verbreekt in drie verschillende componenten: model-, uitzicht, en controleur. De oorspronkelijke implementatie van het patroon definieert dat de weergave verantwoordelijk is voor het weergeven van de gegevens van het model aan de gebruiker. De gebruiker communiceert met de toepassing via de weergavelaag. De controller is verantwoordelijk voor het afhandelen van gebruikersinteractie en het manipuleren van de gegevens van het model als resultaat. Het beeld visualiseert deze wijzigingen voor de gebruiker. Zoals geïllustreerd in het onderstaande diagram, speelt het model een sleutelrol in het MVC-patroon zoals het werd ontworpen door Reenskaug.

MVC en Cocoa

De implementatie die we gebruiken in de ontwikkeling van Cocoa verschilt van het oorspronkelijke ontwerp van Reenskaug. Bekijk het onderstaande diagram om beter te begrijpen wat deze verschillen met zich meebrengen.

Zoals ik eerder al zei, hebben de weergave en de controller een hechte relatie. In een typische iOS-applicatie houdt een controller een sterke verwijzing naar de weergave die het beheert. De weergave is een dom voorwerp dat gegevens kan weergeven en op gebruikersinteractie kan reageren. Het resultaat is een zeer herbruikbare component.

De controller speelt een cruciale rol in Cocoa-toepassingen die worden aangedreven door het Model-View-Controller-patroon. Het neemt een deel van de taken van het model over in de originele MVC-implementatie van Reenskaug. De weergave en het model communiceren niet rechtstreeks met elkaar. In plaats daarvan is het model meestal eigendom van de controller, waarmee het de weergave die het beheert, configureert en vult.

Ik hoop dat je de subtiele verschillen ziet tussen de oorspronkelijke implementatie van Reenskaug in Smalltalk en de Cocoa-implementatie waaraan we gewend zijn geraakt. De verschillen zijn klein, maar zoals ik in een moment zal bespreken, is de impact die ze hebben belangrijk.

3. Het goede: scheiding van zorgen en herbruikbaarheid

Voordat we een blik werpen op de problemen die MVC introduceert, wil ik je laten zien waarom het Model-View-Controller-patroon zo'n populair en wijdverspreid patroon is geworden in softwareontwikkeling. Het Model-View-Controller-patroon dat we gebruiken bij de ontwikkeling van Cocoa heeft een aantal duidelijke voordelen die het heeft geërfd van de oorspronkelijke implementatie van Reenskaug.

Het meest voor de hand liggende voordeel van het patroon Model-View-Controller is een scheiding van zorgen. De weergavelaag is bijvoorbeeld verantwoordelijk voor het presenteren van gegevens aan de gebruiker. Het model en de controllerlagen houden zich niet bezig met gegevenspresentatie. Maar als je MVC hebt gebruikt in een Cocoa-project, dan weet je dat dit niet altijd waar is. Daar zal ik zo dadelijk meer over praten.

Een direct voordeel van deze scheiding van zorgen is herbruikbaarheid. Elk van de componenten van het Model-View-Controller-patroon is gericht op een specifieke taak, wat betekent dat de bouwstenen van een MVC-applicatie vaak gemakkelijk opnieuw kunnen worden gebruikt. Het biedt ook de mogelijkheid om deze componenten losjes te koppelen, waardoor hun herbruikbaarheid wordt vergroot. Dit geldt echter niet voor elk onderdeel. In een Cocoa-project zijn bijvoorbeeld controllers vaak specifiek voor de toepassing en geen goede kandidaten voor hergebruik.

De opvattingen en modellen van een project zijn echter zeer herbruikbaar als ze op de juiste manier zijn ontworpen. Tabel- en verzamelweergaven zijn bijvoorbeeld UIView subklassen die worden gebruikt in miljoenen applicaties. Omdat een tabelweergave gebruikersinteractie delegeert aan een ander object en een gegevensbron vraagt ​​naar de gegevens die het moet weergeven, kan het zich uitsluitend richten op gegevenspresentatie en gebruikersinteractie.

4. The Bad: Massive View Controllers

De meeste ontwikkelaars begrijpen snel wat het Model-View-Controller-patroon naar de tafel brengt en hoe het moet worden geïmplementeerd. Helaas heeft het Model-View-Controller-patroon ook een lelijke kant. Ik schreef al over herbruikbaarheid en scheiding van zorgen. Ik weet zeker dat ik je niet van deze voordelen hoef te overtuigen. Een tafelweergave is zeer herbruikbaar en ongelooflijk performant. Ontwikkelaars kunnen standaard UIKit-componenten in hun toepassingen gebruiken zonder subclassificatie of aanpassing.

De limieten van MVC raken

Maar dat is slechts een deel van het verhaal. Je weet wanneer je de grenzen van MVC begint te betreden wanneer gigantische view controllers je project binnen zijn geslopen. Het is tijd voor verandering wanneer u honderden of duizenden regels code doorploegt om die ene methode te vinden waarnaar u op zoek bent. 

Dumpen in de controller

De meeste ontwikkelaars weten wat er in de weergave en de modellagen van een typische Cocoa-toepassing past die wordt aangedreven door het patroon Model-View-Controller. Maar welk onderdeel is verantwoordelijk voor het formatteren van de gegevens die aan de gebruiker worden getoond? Vergeet niet dat views dom en herbruikbaar moeten zijn. De weergave hoeft geen gegevens op te maken. Rechts? Het moet alleen weten hoe data moet worden gepresenteerd en op gebruikersinteractie moet worden gereageerd. Moet het model te maken hebben met gegevensformattering?

En hoe zit het met netwerken? Dat is zeker niet de taak van het uitzicht. Moet het worden gedelegeerd naar het model? Dat klinkt niet goed. Waarom laten we dat stuk code niet in de controller glijden. Het voelt niet goed, maar het zal het voorlopig wel doen.

Na vele coderegels kom je uit bij een controller die klaar is om te barsten en een nachtmerrie om te testen. Testen? Ik hoor je. Ik zou een view controller niet willen testen massief zicht controller-syndroom een van beide.

5. Een betere oplossing

U bent begonnen met goede bedoelingen, maar u hebt uiteindelijk een project met een verzameling controllers voor overgewicht die moeilijk te beheren en te onderhouden zijn. Je kijkt er niet naar uit om nieuwe functies aan het project waaraan je werkt toe te voegen, omdat het openen van die view controllers je ziek in je maag maakt. Klinkt dit bekend?

Het is belangrijk om te beseffen dat dit een veelvoorkomend scenario is. Veel ontwikkelaars raken de grenzen van het Model-View-Controller-patroon aan en realiseren zich dat ze iets beters nodig hebben. De kans is groot dat u al naar verschillende alternatieven hebt gekeken, zoals MVP (Model-View-Presenter) of MVVM (Model-View-ViewModel).

In de volgende aflevering van deze serie zoom ik in op de Model-View-ViewModel patroon. Het zal u raar bekend voorkomen als u al met het patroon Model-View-Controller hebt gewerkt. Maar het patroon Model-View-ViewModel brengt een paar verbeteringen aan de tabel die heel goed werken voor de ontwikkeling van Cocoa.

En terwijl u wacht, bekijkt u enkele van onze andere berichten over de ontwikkeling van Cocoa-apps!