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:
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.
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:
Nadelen:
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:
Nadelen: