Guardando il video tutorial fornito da Apple, sembra che swift sia una programmazione orientata al protocollo e Apple consiglia ai programmatori di utilizzare il protocollo rispetto alla classe. Ma dal mio punto di vista personale, non vedo vantaggi apparenti per il protocollo. la classe può essere conforme al protocollo, ma può anche ereditare dalla superclasse. Possiamo aggiungere un'estensione al protocollo, ma possiamo anche aggiungere un'estensione alla classe. Possiamo implementare funzioni in classi conformi al protocollo, ma possiamo anche sovrascrivere func in sottoclasse. Sono ancora confuso dal fatto che abbiamo bisogno di usare il protocollo piuttosto che la classe. E quando dovremmo usare il protocollo invece della classe?Perché il protocollo è migliore della classe in swift?
risposta
Facciamo un esempio di download.
Si ha una classe base FileDownloadModel e hanno 3 sottoclassi AudioFileDownloadModel, VideoFileDownloadModel, ImageDownloadModel.
Hai un DownloadManager che prende input come FileDownloadModel e usa la sua urlToDownload proprietà del modello per scaricare il file.
tardi giù la linea si è detto che, c'è un modello più venire, ma la sua, come UserDownloadModel che sottoclasse da utente.
Vedere ora diventa difficile gestire lo scenario in cui è necessario modificare molto codice per incorporare i metodi di download.
Come protocollo di programmazione orientata vi aiuterà qui:
- Creare un protocollo chiamato DownloadingFileProtocol e aggiungere metodi che avete bisogno per il download del file. per esempio. urlToDownload, pathToSave, ampliamento ecc
- Implementare lo stesso protocollo in FileDownloadModel e UserDownloadModel. Vedere il vantaggio che non è necessario modificare un lotto di codice in UserDownloadModel. Implementerai semplicemente i metodi da DownloadingFileProtocol.
- Vedere se ancora una nuova entità viene giù la linea, non sarà modificare qualsiasi codice solo implementare i metodi di protocollo.
- E ora la tua DownloadManager può prendere input come DownloadingFileProtocol invece di un modello specifico e ora si può fare qualsiasi modello come scaricabile.
Con i protocolli, una classe/struct può essere utilizzata come cose diverse. Ad esempio, la struttura String
è conforme a molti protocolli!
Comparable
CustomDebugStringConvertible
Equatable
ExtendedGraphemeClusterLiteralConvertible
Hashable
MirrorPathType
OutputStreamType
Streamable
StringInterpolationConvertible
StringLiteralConvertible
UnicodeScalarLiteralConvertible
Questo significa che String
può essere utilizzato come 11 cose diverse! Quando un metodo ha bisogno di uno qualsiasi dei protocolli indicati sopra come parametro, puoi passare una stringa.
"Ma posso solo creare una classe di Dio che ha tutti i metodi che i protocolli hanno!" hai litigato. Ricorda, puoi ereditare solo da una classe in Swift, e l'ereditarietà multipla è così pericolosa da poter essere utilizzata per rendere il tuo codice estremamente complesso.
Inoltre, con i protocolli, è possibile definire il comportamento personalizzato della classe. Il metodo String
di hashcode
non corrisponde a quello di Int
. Ma sono entrambi compatibili con questa funzione:
func doStuff(x: Hashable) {
}
Questa è la vera meraviglia dei protocolli.
Ultimo ma non meno importante, i protocolli hanno senso. I protocolli rappresentano una relazione "è un tipo di" o "può essere usata come". Quando X può essere usato come Y, ha senso che X sia conforme al protocollo Y, giusto?
In gran parte, è una gerarchia di tipi. Diciamo che avere un oggetto che rappresenta un GlowingRedCube
, ma si vuole avere quel tipo usato in un sacco di codice generico che si preoccupa:
- diverse forme -
Cube
estende versoShape
- diversi colori -
Red
estende versoColorful
- Diversi tipi di illuminazione -
Glowing
estendeIlluminated
Sei nei guai. Puoi scegliere una classe base e aggiungere specializzazioni: GlowingRedCube
estende GlowingCube
estende Shape
, ma poi ottieni un insieme molto ampio di classi e un insieme inflessibile di cose (e se volessi creare un SoftRedCube
, ma tieni i metodi che hai definito per il tuo attuale tipo di cubo rosso?)
Si potrebbe semplicemente avere Cube
e avere illuminazione e forma essere proprietà, ma poi non si ottiene il controllo del tipo di compilatore bello: se si dispone di un metodo Room.lightUp()
e deve passarlo a Cube
, è quindi necessario verificare se tale tipo include qualsiasi illuminazione! Se potessi solo passarlo a Illuminated
allora il compilatore ti fermerebbe non appena lo avresti provato.
I protocolli consentono di separare questo: GlowingRedCube
in grado di implementare il protocollo Illuminated
, il protocollo Colorful
e il protocollo Shape
. A causa delle estensioni del protocollo, è possibile includere implementazioni predefinite di funzionalità, in modo da non dover scegliere un livello di gerarchia a cui collegarlo.
struct GlowingRedCube: Shape, Colorful, Illuminated {
// ..
}
In effetti, i protocolli consentono di collegare un comportamento a un oggetto, indipendentemente da ciò che il resto che fa oggetto.Questo è esattamente il motivo per cui vengono utilizzati per cose come i protocolli delegate e datasource: anche se perlopiù stai allegando quelle cose ad un ViewController
, l'oggetto sottostante non è rilevante, quindi puoi essere flessibile su come implementare.
C'è molto di più nell'usare i protocolli in Swift che solo le basi: sono eccezionalmente potenti perché possono essere collegati a un numero di differenti costrutti di codice: classi, strutture ed enumerazioni. Questo ti permette di avvicinarti davvero al protocollo di programmazione. C'è un ottimo video su questo approccio da WWDC last year, ma aiuta a dedicare un po 'di tempo a provare alcune strutture di oggetti diverse per capire meglio i problemi.
classe e protocollo sono concetti ortogonali. Un protocollo attraversa l'albero delle classi e unisce una o più classi con una discendenza disparata.
Forse Più semplicemente:
- "classe" definisce ciò che un oggetto è.
- "protocollo" definisce un comportamento che l'oggetto ha.
in modo da avere una classe di auto:
class Car {
var bodyStyle : String
}
e una classe Colore:
class Color {
var red : Int
var green : Int
var blue : Int
}
Ora, più o meno, ovviamente, colori e automobili sono completamente indipendenti, tuttavia, supponiamo che voglio essere in grado di convertire facilmente uno a Stringhe, quindi posso eseguire il debug con:
print(Car(...))
o
print(Color(...))
proprio per questo scopo, la lingua Swift definisce il protocollo CustomStringConvertible
in modo che possiamo dichiarare l'auto può essere stampato utilizzando quel protocollo:
extension Car : CustomStringConvertible {
var description : String { get { return "Car: \(bodyStyle)" } }
}
e un colore come pure :
extension Color : CustomStringConvertible {
var description : String { get { return "Color: \(red) \(green) \(blue)" } }
}
Allora, dove prima che io avrei bisogno di un metodo di stampa per ogni classe, ora ho solo bisogno di un metodo di stampa che sembra qualcosa di l ike:
func print(data:CustomStringConvertible) {
let string = data.description
... bunch of code to actually print the line
}
Questo è possibile perché dichiarando che una classe implementa un protocollo è una promessa che posso utilizzare i metodi dal protocollo, sapendo che stanno attuate e (presumibilmente) fanno quello che ci si aspetta.
ma non sei riuscito a conformarti anche a più protocolli in ObjC? In che modo Swift è diverso? – Honey
Ecco il mio esempio: se abbiamo 'Car' che eredita da' Vehicle' e 'Wood' che eredita da' BuildingMaterial', e vogliamo aggiungere il metodo 'burn' a' Car' e 'Wood', quindi un protocollo sarebbe la soluzione migliore. – tktsubota
Non sono sicuro di quale video ti riferisci, ma la [Programm-Oriented Programming] del WWDC 2015 (https://developer.apple.com/videos/play/wwdc2015/408/) descrive, in alcuni dettagli, il meriti dei protocolli. – Rob