La composizione dei protocolli è un meccanismo in Swift che consente di creare un tipo che deve soddisfare più protocolli contemporaneamente. È un modo alternativo per sostituire l'ereditarietà multipla, che non esiste in Swift per le classi.
Storia della questione
Objective-C supportava l'ereditarietà multipla solo per i protocolli, ma non per le classi. Swift ha sviluppato questa tradizione, concentrandosi sui protocolli e sulla loro combinazione per costruire nuove astrazioni.
Problema
Spesso i programmatori devono creare un tipo il cui comportamento è definito da più astrazioni. L'ereditarietà multipla porta inevitabilmente a conflitti di gerarchie, e in Swift questo viene risolto in modo sicuro attraverso i protocolli e la composizione dei protocolli.
Soluzione
In Swift è possibile combinare protocolli utilizzando l'operatore '&'. Ciò consente di creare variabili o parametri di funzione che devono conformarsi a più protocolli contemporaneamente.
Esempio di codice:
protocol Drivable { func drive() } protocol Flyable { func fly() } struct FlyingCar: Drivable, Flyable { func drive() { print("Driving") } func fly() { print("Flying") } } func testVehicle(_ vehicle: Drivable & Flyable) { vehicle.drive() vehicle.fly() } testVehicle(FlyingCar())
Caratteristiche principali:
È possibile passare un oggetto che implementa solo uno dei protocolli a un parametro con composizione di protocolli?
No, l'oggetto deve implementare tutti i protocolli coinvolti nella composizione, altrimenti il compilatore genererà un errore.
Esempio di codice:
// struct Car: Drivable {} — non può essere passato a testVehicle, poiché fly() non è implementato
Funziona la composizione dei protocolli con i tipi, e non solo con i valori?
La composizione dei protocolli si applica ai valori (variabili, parametri di funzioni), ma non viene utilizzata nella definizione del tipo di oggetto (ad esempio, non è possibile dichiarare un nuovo tipo come una "composizione" di protocolli — solo una variabile).
Esempio di codice:
var obj: SomeProtocol & AnotherProtocol // consentito // typealias MyType = SomeClass & AnotherProtocol // errore
È possibile combinare classi e protocolli tramite &?
Sì, ma con una limitazione: solo un tipo di classe (classe o suo erede) può essere a sinistra, gli altri devono essere solo protocolli, altrimenti il compilatore genererà un errore.
Esempio di codice:
class A {} protocol B {} // func f(obj: A & B) {} // consentito // func f(obj: A & AnotherClass & B) {} // errore! Solo un tipo di classe è consentito
Nel progetto per il trasferimento dei dati tra i livelli vengono utilizzati oggetti con composizione dei protocolli, sebbene sarebbe bastato un solo protocollo:
func present(item: Displayable & Serializable) { ... }
Pro:
Utilizzo della composizione dei protocolli solo in casi espliciti — ad esempio, trattamento di oggetti che supportano sia Codable che Identifiable per la serializzazione generica:
func save<T: Codable & Identifiable>(_ item: T) { ... }
Pro: