ProgrammatieSwift middle-ontwikkelaar

Wat is protocol composition in Swift, hoe werkt het en waarvoor is het nodig? Wat zijn de valkuilen bij het gebruik van meerdere protocollen tegelijkertijd?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord.

In Swift is de ervaring van veel OOP-talen samengevoegd en verbeterd door de mogelijkheid om protocollen te combineren in plaats van alleen te erven. Protocolcompositie maakt het mogelijk om een variabele, een functieparameter of een generiek type te declareren met de eis van overeenstemming met meerdere protocollen tegelijk. Dit mechanisme is uiterst nuttig wanneer je moet werken met objecten die het gedrag van meerdere contracten (interfaces) hebben, terwijl je flexibel de nadelen van meervoudige overerving kunt vermijden. Het probleem dat door compositie wordt opgelost, is de noodzaak om "een object moet voldoen aan een groep vereisten" uit te drukken, en niet alleen aan één.

In Swift wordt een speciale syntaxis gebruikt: de combinatie van protocollen met het teken & (ampersand), bijvoorbeeld protocolA & protocolB. Onder de motorkap wordt er een runtime-check uitgevoerd (bijvoorbeeld bij typecasting en in generieke contexten). Dit minimaliseert het aantal types en implementeert flexibel het patroon "scheiding van verantwoordelijkheden".

Voorbeeld code:

protocol Drawable { func draw() } protocol Movable { func move() } struct Sprite: Drawable, Movable { func draw() { print("Sprite tekent") } func move() { print("Sprite beweegt") } } func animate(object: Drawable & Movable) { object.draw() object.move() } let s = Sprite() animate(object: s)

Belangrijke kenmerken:

  • Staat flexibele expressie van gedragscompositie toe zonder erfenis hiërarchieën
  • Garandeert de onmiddellijke uitvoering van alle contracten
  • Compatibel met generieke parameters en type-aliases

Valkuil vragen.

Is het mogelijk om een variabele van het type alleen protocolA & protocolB te maken zonder je aan een specifieke structuur of klasse te binden?

Ja, je kunt een variabele declareren als overeenkomend met meerdere protocollen, bijvoorbeeld:

var obj: protocolA & protocolB

Maar belangrijk: dergelijke variabelen kunnen alleen verwijzen naar objecten (en niet naar waarde types), als in de compositie ten minste één protocol beperkt is tot klassetypes (protocol: AnyObject).

Kan een klassentype worden opgenomen in de compositie, bijvoorbeeld SomeClass & Drawable?

Ja, maar met nuances: een compositie van het type SomeClass & Protocol vereist dat de waarden noodzakelijkerwijs instanties van die klasse zijn (of hun afgeleiden) die het protocol implementeren. Deze aanpak wordt gebruikt om generieke types te beperken.

Kan protocolcompositie worden gebruikt als geassocieerd type in protocolextensies?

Ja, maar er zijn beperkingen: je kunt geen associatedtype als compositie declareren, maar je kunt waar gebruiken bij extensies om protocollen-compositiebepalingen te beperken, bijvoorbeeld een extensie die alleen van toepassing is op types die voldoen aan meerdere protocollen.

Typische fouten en anti-patronen

  • Gebruik van compositie met acht of negen protocollen: dit is een teken van architectonische overbelasting en slechte verantwoordelijkheidsverdeling
  • Een value type (struct) casten naar een variabele van protocolcompositie met de beperking AnyObject resulteert altijd in een fout
  • Het gebruik van dezelfde compositie op verschillende delen van de applicatie zonder typealias: bemoeilijkt de leesbaarheid

Voorbeeld uit het leven

Negatieve casus

In een project zijn 5 vergelijkbare protocollen geïmplementeerd - Drawable, Movable, Resizable, Colorable, Animatable. Overal werd de compositie Drawable & Movable & Resizable & Colorable & Animatable toegepast. Typische fouten gingen gepaard met complexe bugs omdat sommige entiteiten niet één van de contracten implementeerden.

Voordelen:

  • Geen diepe overerving vereist
  • Makkelijk om functionaliteit toe te voegen of te verwijderen

Nadelen:

  • Moeilijk om inconsistenties te volgen
  • Moeilijke testbaarheid
  • Slechte leesbaarheid van de declaratie

Positieve casus

In plaats van een complexe compositie zijn er twee hoofdprotocollen onderscheiden (bijvoorbeeld Actor en Viewable), is er een typealias gemaakt voor de compositie "DynamicEntity" en is deze overal gebruikt. Verantwoordelijkheden zijn duidelijk afgebakend.

Voordelen:

  • De code is leesbaar en gemakkelijker te onderhouden
  • Tests onderscheiden duidelijk het gedrag voor DynamicEntity
  • Snelle aanpassing van de eisenlijst

Nadelen:

  • Vereist een heroverweging van de architectuur
  • Soms moeten bestaande klassen worden gesplitst om aan de vereisten te voldoen