Programmazione orientata al protocollo in Swift 2

introduzione

Con l'uscita di Swift 2, Apple ha aggiunto una serie di nuove funzionalità e funzionalità al linguaggio di programmazione Swift. Uno dei più importanti, tuttavia, era una revisione dei protocolli. La migliore funzionalità disponibile con i protocolli Swift consente un nuovo tipo di programmazione, programmazione orientata ai protocolli. Ciò è in contrasto con il più comune stile di programmazione orientato agli oggetti a cui molti di noi sono abituati.

In questo tutorial, ti mostrerò le basi della programmazione orientata ai protocolli in Swift e in che modo si differenzia dalla programmazione orientata agli oggetti.

Prerequisiti

Questo tutorial richiede l'esecuzione di Xcode 7 o versioni successive, che include il supporto per la versione 2 del linguaggio di programmazione Swift.

1. Nozioni di base sul protocollo

Se non hai già familiarità con i protocolli, sono un modo per estendere la funzionalità di una classe o di una struttura esistente. Un protocollo può essere pensato come un progetto o un'interfaccia che definisce un insieme di proprietà e metodi. È richiesta una classe o una struttura conforme a un protocollo per compilare queste proprietà e metodi con valori e implementazioni rispettivamente.

Va inoltre notato che ognuna di queste proprietà e questi metodi possono essere designati come facoltativi, il che significa che non sono richiesti tipi conformi per implementarli. Una definizione del protocollo e la conformità di classe in Swift potrebbe assomigliare a questo:

protocollo Benvenuto var welcomeMessage: String get set opzionale func welcome () Welcomer: Welcome var welcomeMessage = "Hello World!" func welcome () print (welcomeMessage)

2. Un esempio

Per iniziare, apri Xcode e crea un nuovo parco giochi per iOS o OS X. Dopo che Xcode ha creato il parco giochi, sostituiscilo con il seguente:

protocollo Drivable var topSpeed: Protocollo Int get Reversibile var reverseSpeed: Protocollo Int get Transport var seatCount: Int get

Definiamo tre protocolli, ciascuno contenente una proprietà. Successivamente, creiamo una struttura conforme a questi tre protocolli. Aggiungi il seguente codice al parco giochi:

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

Potresti aver notato che invece di creare una classe conforme a questi protocolli, abbiamo creato una struttura. Lo facciamo per evitare uno dei problemi tipici inerenti alla programmazione orientata agli oggetti, riferimenti agli oggetti.

Immagina, per esempio, di avere due oggetti, A e B. A crea alcuni dati da solo e mantiene un riferimento a quei dati. A condivide quindi questi dati con B per riferimento, il che significa che entrambi gli oggetti hanno un riferimento allo stesso oggetto. Senza una conoscenza, B modifica i dati in qualche modo.

Anche se questo potrebbe non sembrare un grosso problema, può essere quando A non si aspettava che i dati fossero modificati. L'oggetto A può trovare dati che non sa come gestire o gestire. Questo è un rischio comune di riferimenti a oggetti.

In Swift, le strutture sono passate valore piuttosto che da riferimento. Ciò significa che, nell'esempio precedente, se i dati creati da A sono stati impacchettati come struttura anziché oggetto e condivisi con B, i dati verrebbero copiati anziché condivisi per riferimento. Ciò comporterebbe quindi che A e B abbiano la loro copia unica dello stesso pezzo di dati. Una modifica apportata da B non influisce sulla copia gestita da A.

Spezzare il GuidabileReversibile, e Trasporto i componenti nei singoli protocolli consentono anche un maggiore livello di personalizzazione rispetto all'ereditarietà tradizionale delle classi. Se hai letto il mio primo tutorial sul nuovo framework GameplayKit in iOS 9, questo modello orientato al protocollo è molto simile alla struttura Entità e Componenti utilizzata nel framework GameplayKit.

Adottando questo approccio, i tipi di dati personalizzati possono ereditare funzionalità da più fonti anziché da una singola superclasse. Tenendo presente ciò che abbiamo ottenuto finora, potremmo creare le seguenti classi:

  • una classe con componenti del GuidabileReversibile protocolli
  • una classe con componenti del Guidabile e Trasportabile protocolli
  • una classe con componenti del ReversibileTrasportabile protocolli

Con la programmazione orientata agli oggetti, il modo più logico per creare queste tre classi sarebbe ereditare da una superclasse che contiene i componenti di tutti e tre i protocolli. Questo approccio, tuttavia, fa sì che la superclasse sia più complicata di quanto dovrebbe essere e ciascuna delle sottoclassi eredita più funzionalità di quelle necessarie.

3. Estensioni del protocollo

Tutto ciò che ti ho mostrato finora è stato possibile in Swift sin dalla sua uscita nel 2014. Questi stessi concetti orientati al protocollo potrebbero essere stati applicati anche ai protocolli Objective-C. A causa delle limitazioni che esistevano sui protocolli, tuttavia, la vera programmazione orientata al protocollo non era possibile fino a quando non sono state aggiunte alcune funzionalità chiave nella lingua Swift nella versione 2. Una delle più importanti di queste funzioni è estensioni di protocollo, Compreso estensioni condizionali.

Innanzitutto, estendiamo il Guidabile protocollo e aggiungere una funzione per determinare se un particolare Guidabile è più veloce di un altro Aggiungi il seguente al tuo parco giochi:

estensione Drivable func isFasterThan (item: Drivable) -> Bool return self.topSpeed> item.topSpeed lascia sedan = Car () let sportsCar = Car (topSpeed: 250, reverseSpeed: 25, seatCount: 2) sedan.isFasterThan (auto sportiva)

Puoi vedere che, quando il codice del campo da giuoco viene eseguito, emette un valore di falsocome il tuo berlina la macchina ha un valore predefinito Topspeed di 150, che è inferiore al auto sportiva.

Potresti aver notato che abbiamo fornito una funzione definizione piuttosto che una funzione dichiarazione. Questo sembra strano, perché i protocolli dovrebbero contenere solo dichiarazioni. Destra? Questa è un'altra caratteristica molto importante delle estensioni del protocollo in Swift 2, comportamenti di default. Estendendo un protocollo, è possibile fornire un'implementazione predefinita per funzioni e proprietà calcolate in modo che le classi conformi al protocollo non debbano.

Successivamente, ne definiremo un altro Guidabile estensione del protocollo, ma questa volta la definiremo solo per i tipi di valore che sono conformi anche a Reversibile protocollo. Questa estensione conterrà quindi una funzione che determina quale oggetto ha il migliore intervallo di velocità. Possiamo ottenere questo con il seguente codice:

estensione Drivable dove Self: Reversible func hasLargerRangeThan (item: Self) -> Bool return (self.topSpeed ​​+ self.reverseSpeed)> (item.topSpeed ​​+ item.reverseSpeed) sportsCar.hasLargerRangeThan (berlina)

Il Se stesso parola chiave, digitata con una "S" maiuscola, viene utilizzata per rappresentare la classe o la struttura conforme al protocollo. Nell'esempio sopra, il Se stesso parola chiave rappresenta il Auto struttura.

Dopo aver eseguito il codice del parco giochi, Xcode mostrerà i risultati nella barra laterale a destra, come mostrato di seguito. Nota che auto sportiva ha una gamma più ampia di berlina.

4. Lavorare con la libreria standard di Swift

Sebbene la definizione e l'estensione dei propri protocolli possano essere molto utili, il vero potere delle estensioni del protocollo viene mostrato quando si lavora con la libreria standard di Swift. Ciò consente di aggiungere proprietà o funzioni a protocolli esistenti, come ad esempio CollectionType (usato per cose come array e dizionari) e equatable (essere in grado di determinare quando due oggetti sono uguali o meno). Con le estensioni di protocollo condizionali, puoi anche fornire funzionalità molto specifiche per un tipo specifico di oggetto conforme a un protocollo.

Nel nostro parco giochi, estenderemo il CollectionType protocollo e creare due metodi, uno per ottenere la massima velocità media delle auto in a Auto array e un altro per la velocità media inversa. Aggiungi il seguente codice al tuo parco giochi:

estensione CollectionType dove Self.Generator.Element: Drivable func averageTopSpeed ​​() -> Int var total = 0, count = 0 per l'elemento in self total + = item.topSpeed ​​count ++ return (totale / count) func averageReverseSpeed(articoli: T) -> Int var total = 0, count = 0 per articolo in items total + = item.reverseSpeed ​​count ++ return (total / count) let cars = [Car (), berlina, sportscar] auto .averageTopSpeed ​​() averageReverseSpeed ​​(macchine)

L'estensione del protocollo che definisce il averageTopSpeed il metodo sfrutta le estensioni condizionali in Swift 2. Al contrario, il averageReverseSpeed la funzione che definiamo direttamente sotto di esso è un altro modo per ottenere un risultato simile utilizzando i generici di Swift. Personalmente preferisco l'aspetto più pulito CollectionType estensione del protocollo, ma dipende dalle preferenze personali.

In entrambe le funzioni, iteriamo attraverso l'array, sommiamo l'importo totale e quindi restituiamo il valore medio. Si noti che manterremo manualmente un conteggio degli elementi nell'array, perché quando si lavora con CollectionType piuttosto che regolare schieramento digitare gli elementi, il contare la proprietà è a Self.Index.Distance digitare il valore piuttosto che un Int.

Una volta che il tuo parco giochi ha eseguito tutto questo codice, dovresti vedere una velocità massima in uscita di 183 e una velocità media inversa di 21.

5. Importanza delle classi

Nonostante la programmazione orientata al protocollo sia un modo molto efficiente e scalabile per gestire il tuo codice in Swift, ci sono ancora ragioni perfettamente valide per l'utilizzo delle classi durante lo sviluppo in Swift:

Compatibilità all'indietro

La maggior parte degli SDK iOS, watchOS e tvOS sono scritti in Objective-C, usando un approccio orientato agli oggetti. Se è necessario interagire con una delle API incluse in questi SDK, si è costretti a utilizzare le classi definite in questi SDK.

Fare riferimento a un file o articolo esterno

Il compilatore Swift ottimizza la durata degli oggetti in base a quando e dove vengono utilizzati. La stabilità degli oggetti basati sulla classe significa che i tuoi riferimenti ad altri file e oggetti rimarranno coerenti.

Riferimenti dell'oggetto

I riferimenti oggetto sono esattamente ciò di cui hai bisogno, ad esempio, se stai inserendo informazioni in un particolare oggetto, ad esempio un renderer grafico. Usare le classi con condivisione implicita è importante in situazioni come questa, perché devi essere sicuro che il renderer a cui stai inviando i dati sia sempre lo stesso renderer di prima.

Conclusione

Si spera che alla fine di questo tutorial si possa vedere il potenziale della programmazione orientata ai protocolli in Swift e in che modo può essere utilizzato per ottimizzare ed estendere il codice. Mentre questa nuova metodologia di codifica non sostituirà interamente la programmazione orientata agli oggetti, essa offre una serie di nuove possibilità molto utili.

Dai comportamenti di default alle estensioni del protocollo, la programmazione orientata al protocollo in Swift verrà adottata da molte API future e cambierà completamente il modo in cui pensiamo allo sviluppo del software.

Come sempre, assicurati di lasciare i tuoi commenti e feedback nei commenti qui sotto.