Protocol-georiënteerd programmeren in Swift 2

Invoering

Met de release van Swift 2 heeft Apple een reeks nieuwe functies en mogelijkheden toegevoegd aan de Swift-programmeertaal. Een van de belangrijkste was echter een herziening van de protocollen. De verbeterde functionaliteit die beschikbaar is met Swift-protocollen maakt een nieuw type programmeren, protocolgeoriënteerd programmeren mogelijk. Dit staat in contrast met de meer algemene objectgeoriënteerde programmeerstijl die velen van ons gewend zijn.

In deze tutorial laat ik je de basis zien van protocolgeoriënteerd programmeren in Swift en hoe het verschilt van objectgeoriënteerd programmeren.

voorwaarden

Deze tutorial vereist dat je Xcode 7 of hoger gebruikt, inclusief ondersteuning voor versie 2 van de Swift-programmeertaal.

1. Basisprincipes van het protocol

Als u nog niet bekend bent met protocollen, zijn ze een manier om de functionaliteit van een bestaande klasse of structuur uit te breiden. Een protocol kan worden gezien als een blauwdruk of interface die een reeks eigenschappen en methoden definieert. Een klasse of structuur die voldoet aan een protocol is vereist om deze eigenschappen en methoden in te vullen met respectievelijk waarden en implementaties.

Er moet ook worden opgemerkt dat een van deze eigenschappen en methoden als optioneel kan worden aangemerkt, wat betekent dat er geen conforme typen nodig zijn om ze uit te voeren. Een protocoldefinitie en klasse-conformiteit in Swift kunnen er als volgt uitzien:

protocol Welkom var welcomeMessage: String get set optioneel func welcome () class Welcomer: Welcome var welcomeMessage = "Hello World!" func welcome () print (welcomeMessage)

2. Een voorbeeld

Om te beginnen opent u Xcode en maakt u een nieuwe speeltuin voor iOS of OS X. Nadat Xcode de speeltuin heeft gemaakt, vervangt u de inhoud door het volgende:

protocol Drivable var topSpeed: Int get protocol Omkeerbaar var reverseSpeed: Int get protocol Transport var seatCount: Int get

We definiëren drie protocollen die elk een eigenschap bevatten. Vervolgens maken we een structuur die voldoet aan deze drie protocollen. Voeg de volgende code toe aan de speeltuin:

struct Auto: Drivable, Reversible, Transport var topSpeed ​​= 150 var reverseSpeed ​​= 20 var seatCount = 5

Je hebt misschien gemerkt dat we een structuur hebben gemaakt in plaats van een klasse te maken die voldoet aan deze protocollen. We doen dit om een ​​van de typische problemen te voorkomen die inherent zijn aan objectgeoriënteerd programmeren, objectreferenties.

Stel u bijvoorbeeld voor dat u twee objecten hebt, A en B. A maakt enkele gegevens op zichzelf en houdt een verwijzing naar die gegevens bij. A deelt deze gegevens vervolgens met B als referentie, wat betekent dat beide objecten een verwijzing naar hetzelfde object hebben. Zonder een weten, verandert B de gegevens op de een of andere manier.

Hoewel dit misschien geen groot probleem lijkt, kan het zijn dat A niet verwachtte dat de gegevens zouden worden gewijzigd. Object A kan gegevens vinden waarvan het niet weet hoe het moet omgaan of waarmee het moet omgaan. Dit is een algemeen risico van objectreferenties.

In Swift worden structuren gepasseerd waarde in plaats van door referentie. Dit betekent dat in het bovenstaande voorbeeld, als de gegevens die door A zijn gemaakt, werd verpakt als een structuur in plaats van een object en gedeeld met B, de gegevens zouden worden gekopieerd in plaats van gedeeld door verwijzing. Dit zou er vervolgens toe leiden dat zowel A als B hun eigen unieke kopie van hetzelfde stuk gegevens hebben. Een wijziging door B zou geen invloed hebben op de kopie beheerd door A.

Het doorbreken van de verrijdbaaromkeerbaar, en Vervoer componenten in individuele protocollen maakt ook meer maatwerk mogelijk dan traditionele klasse-overerving. Als je mijn eerste tutorial over het nieuwe GameplayKit-framework in iOS 9 hebt gelezen, lijkt dit protocolgeoriënteerde model sterk op de structuur Entities en Componenten die in het GameplayKit-framework wordt gebruikt.

Door deze aanpak toe te passen, kunnen aangepaste gegevenstypen functionaliteit van meerdere bronnen overnemen in plaats van een enkele superklasse. Met in het achterhoofd wat we tot nu toe hebben gemaakt, kunnen we de volgende klassen maken:

  • een klasse met componenten van de verrijdbaar en omkeerbaar protocollen
  • een klasse met componenten van de verrijdbaar en vervoerbaar protocollen
  • een klasse met componenten van de omkeerbaar en vervoerbaar protocollen

Met objectgeoriënteerd programmeren, zou de meest logische manier om deze drie klassen te creëren, zijn om te erven van één superklasse die de componenten van alle drie de protocollen bevat. Deze benadering resulteert er echter in dat de superklasse ingewikkelder is dan het moet zijn en dat elk van de subklassen meer functionaliteit erven dan het nodig heeft.

3. Protocoluitbreidingen

Alles wat ik tot nu toe heb laten zien, is mogelijk sinds de release in 2014 in Swift. Deze protocolgeoriënteerde concepten konden zelfs zijn toegepast op Objective-C-protocollen. Vanwege de beperkingen die in protocollen bestonden, was echte protocolgeoriënteerde programmering echter niet mogelijk totdat een aantal belangrijke functies in versie 2 aan de Swift-taal werden toegevoegd. Een van de belangrijkste van deze functies is protocol uitbreidingen, inclusief voorwaardelijke uitbreidingen.

Allereerst, laten we het uitbreiden verrijdbaar protocol en voeg een functie toe om te bepalen of een bepaalde al dan niet een is verrijdbaar is sneller dan een andere. Voeg het volgende toe aan je speeltuin:

extension Drivable func isFasterThan (item: Drivable) -> Bool return self.topSpeed> item.topSpeed let sedan = Car () laat sportsCar = Auto (topSpeed: 250, reverseSpeed: 25, seatCount: 2) sedan.isFasterThan (sportwagen)

Je kunt zien dat wanneer de code van de speeltuin wordt uitgevoerd, deze een waarde van uitvoert valsals jouw sedan auto heeft een standaardwaarde top snelheid van 150, wat minder is dan de sportwagen.

Je hebt misschien gemerkt dat we een functie hebben geleverd definitie in plaats van een functie verklaring. Dit lijkt vreemd, omdat protocollen alleen verklaringen zouden moeten bevatten. Rechts? Dit is een andere zeer belangrijke functie van protocol-uitbreidingen in Swift 2, standaard gedrag. Door een protocol uit te breiden, kunt u een standaardimplementatie bieden voor functies en berekende eigenschappen, zodat klassen die aan het protocol voldoen niet hoeven te doen.

Vervolgens gaan we een andere definiëren verrijdbaar protocolextensie, maar deze keer definiëren we deze alleen voor waardetypen die ook voldoen aan de omkeerbaar protocol. Deze extensie bevat dan een functie die bepaalt welk object het beste snelheidsbereik heeft. We kunnen dit bereiken met de volgende code:

extension Drivable where Self: Reversible func hasLargerRangeThan (item: Self) -> Bool return (self.topSpeed ​​+ self.reverseSpeed)> (item.topSpeed ​​+ item.reverseSpeed) sportsCar.hasLargerRangeThan (sedan)

De Zelf sleutelwoord, gespeld met een hoofdletter "S", wordt gebruikt om de klasse of structuur weer te geven die overeenkomt met het protocol. In het bovenstaande voorbeeld, de Zelf sleutelwoord vertegenwoordigt het Auto structuur.

Na het uitvoeren van de code van de speelplaats, zal Xcode de resultaten weergeven in de zijbalk aan de rechterkant, zoals hieronder getoond. Let daar op sportwagen heeft een groter bereik dan sedan.

4. Werken met de Swift-standaardbibliotheek

Hoewel het definiëren en uitbreiden van uw eigen protocollen zeer nuttig kan zijn, wordt de ware kracht van protocol-extensies weergegeven wanneer u met de Swift-standaardbibliotheek werkt. Hiermee kunt u eigenschappen of functies toevoegen aan bestaande protocollen, zoals CollectionType (gebruikt voor zaken als arrays en woordenboeken) en gelijk te stellen (kunnen bepalen wanneer twee objecten gelijk zijn of niet). Met voorwaardelijke protocoluitbreidingen kunt u ook zeer specifieke functionaliteit bieden voor een specifiek type object dat voldoet aan een protocol.

In onze speeltuin breiden we de CollectionType protocol en maak twee methoden, een om de gemiddelde topsnelheid van auto's in een te krijgen Auto array en een andere voor de gemiddelde snelheid achteruit. Voeg de volgende code toe aan je speeltuin:

extension CollectionType waarbij Self.Generator.Element: Drivable func averageTopSpeed ​​() -> Int var total = 0, count = 0 for item in self total + = item.topSpeed ​​count ++ return (total / count) func averageReverseSpeed(items: T) -> Int var total = 0, count = 0 voor item in items total + = item.reverseSpeed ​​count ++ return (total / count) let cars = [Car (), sedan, sportsCar] cars .averageTopSpeed ​​() averageReverseSpeed ​​(auto's)

De protocolextensie die de averageTopSpeed methode maakt gebruik van voorwaardelijke uitbreidingen in Swift 2. Daarentegen is de averageReverseSpeed functie die we direct hieronder definiëren, is een andere manier om een ​​vergelijkbaar resultaat te bereiken met behulp van Swift-generieke geneesmiddelen. Ik geef er persoonlijk de voorkeur aan om er schoner uit te zien CollectionType protocoluitbreiding, maar het is aan persoonlijke voorkeur.

In beide functies herhalen we de array, tellen het totale bedrag bij elkaar op en retourneren we de gemiddelde waarde. Houd er rekening mee dat we handmatig een telling bijhouden van de items in de array, omdat dit werkt CollectionType in plaats van normaal reeks typ items, de tellen eigendom is een Self.Index.Distance type waarde in plaats van een Int.

Zodra uw speeltuin al deze code heeft uitgevoerd, ziet u een gemiddelde outputsnelheid van 183 en een gemiddelde snelheid van 21.

5. Belang van klassen

Ondanks dat protocolgeoriënteerde programmering een zeer efficiënte en schaalbare manier is om uw code in Swift te beheren, zijn er nog steeds prima geldige redenen om lessen te gebruiken bij het ontwikkelen in Swift:

Backwards compatibiliteit

De meerderheid van de iOS, watchOS en tvOS SDK's zijn geschreven in Objective-C, met behulp van een object-georiënteerde benadering. Als u moet communiceren met een van de API's die zijn opgenomen in deze SDK's, bent u genoodzaakt om de klassen te gebruiken die zijn gedefinieerd in deze SDK's.

Verwijzen naar een extern bestand of item

De Swift-compiler optimaliseert de levensduur van objecten op basis van waar en wanneer ze worden gebruikt. De stabiliteit van op klassen gebaseerde objecten betekent dat uw verwijzingen naar andere bestanden en items consistent blijven.

Objectreferenties

Objectreferenties zijn precies wat u nodig hebt, bijvoorbeeld als u informatie invoert in een bepaald object, zoals een grafische renderer. Het gebruik van klassen met impliciet delen is belangrijk in situaties als deze, omdat je er zeker van moet zijn dat de renderer waarnaar je de gegevens verstuurt nog steeds dezelfde renderer is als hiervoor.

Conclusie

Hopelijk zie je tegen het einde van deze tutorial het potentieel van protocolgeoriënteerd programmeren in Swift en hoe het kan worden gebruikt om je code te stroomlijnen en uit te breiden. Hoewel deze nieuwe methodologie van coderen object-georiënteerd programmeren niet volledig zal vervangen, brengt het wel een aantal zeer nuttige, nieuwe mogelijkheden.

Van standaardgedrag tot protocolextensies, protocolgeoriënteerd programmeren in Swift zal door veel toekomstige API's worden overgenomen en zal de manier waarop we denken over softwareontwikkeling volledig veranderen.

Laat zoals altijd uw opmerkingen en feedback achter in de opmerkingen hieronder.