Wenn man sich das Video-Tutorial von Apple anschaut, scheint es, dass swift eine protokollorientierte Programmiersprache ist, und Apple ermutigt Programmierer, Protokoll als Klasse zu verwenden. Aber aus meiner persönlichen Sicht sehe ich keine offensichtlichen Vorteile für das Protokoll. Die Klasse kann dem Protokoll entsprechen, aber sie können auch von der Oberklasse erben. Wir können dem Protokoll eine Erweiterung hinzufügen, aber wir können der Klasse auch eine Erweiterung hinzufügen. Wir können Funktionen in Klassen implementieren, die dem Protokoll entsprechen, aber wir können auch func in der Unterklasse überschreiben. Ich bin immer noch verwirrt, warum wir Protokoll statt Klasse verwenden müssen. Und wann sollten wir Protokoll anstelle von Klasse verwenden?
Nehmen wir ein Download-Beispiel.
Sie haben eine Basisklasse FileDownloadModel und haben 3 Unterklassen AudioFileDownloadModel, VideoFileDownloadModel, ImageDownloadModel .
Sie haben einen DownloadManager , der Eingaben als FileDownloadModel akzeptiert und die urlToDownload -Eigenschaft des Modells zum Herunterladen der Datei verwendet.
Später wird Ihnen gesagt, dass es ein weiteres Modell gibt, aber es kommt wie UserDownloadModel welche Unterklasse von Benutzer .
Sehen Sie, jetzt wird es schwierig, mit einem solchen Szenario fertig zu werden, in dem Sie viel Code ändern müssen, um Download-Methoden einzubinden.
Wie Ihnen die protokollorientierte Programmierung hier hilft:
Klasse und Protokoll sind orthogonale Konzepte. Ein Protokoll schneidet quer durch den Klassenbaum und verbindet eine oder mehrere Klassen mit einem unterschiedlichen Ursprung.
Vielleicht einfacher gesagt:
Sie haben also eine Klasse Auto:
%Vor%und eine Klasse Color:
%Vor%Jetzt, mehr oder weniger offensichtlich, Farben und Autos sind völlig unabhängig, aber nehmen wir an, ich möchte in der Lage sein, einfach eins in Strings umzuwandeln, also kann ich debuggen mit:
%Vor%oder
%Vor% Genau zu diesem Zweck definiert die Swift-Sprache das Protokoll CustomStringConvertible
, so dass wir dann deklarieren können, dass ein Auto unter Verwendung dieses Protokolls gedruckt werden kann:
und eine Farbe auch:
%Vor%Wo ich vorher eine Druckmethode für jede Klasse gebraucht hätte, brauche ich jetzt nur noch eine Druckmethode, die ungefähr so aussieht:
%Vor%Dies ist möglich, weil die Deklaration, dass eine Klasse ein Protokoll implementiert, ein Versprechen ist, dass ich die Methoden des Protokolls verwenden kann, in dem Wissen, dass sie implementiert sind und (vermutlich) tun, was erwartet wird.
Hauptsächlich ist es die Hierarchie der Typen. Nehmen wir an, Sie haben ein Objekt, das ein GlowingRedCube
darstellt, aber Sie möchten, dass dieser Typ in vielen generischen Code verwendet wird, der Folgendes interessiert:
Cube
erweitert Shape
Red
erweitert Colorful
Glowing
erweitert Illuminated
Sie sind in Schwierigkeiten. Du könntest eine Basisklasse wählen und Spezialisierungen hinzufügen: GlowingRedCube
erweitert GlowingCube
erweitert Shape
, aber dann erhältst du eine sehr große Menge von Klassen und eine unflexible Menge von Dingen (was ist, wenn du ein SoftRedCube
machen willst) , aber behalten Sie alle Methoden, die Sie für Ihre vorhandene Art von roten Würfel definiert haben?)
Du könntest einfach Cube
haben und Illumination und Shape Eigenschaften haben, aber dann bekommst du keine nette Überprüfung des Compilertyps: Wenn du eine Room.lightUp()
-Methode hast und ihr eine Cube
übergeben musst, dann tust du es muss überprüft werden, ob dieser Typ Beleuchtung enthält! Wenn Sie nur ein Illuminated
übergeben könnten, würde der Compiler Sie stoppen, sobald Sie es versucht haben.
Protokolle erlauben Ihnen, dies zu trennen: GlowingRedCube
kann das Protokoll Illuminated
, das Protokoll Colorful
und das Protokoll Shape
implementieren. Aufgrund von Protokollerweiterungen können Sie Standardimplementierungen von Funktionen hinzufügen, sodass Sie keine Hierarchieebene zum Anhängen auswählen müssen.
Im Prinzip können Sie mit Protokollen ein Verhalten an ein Objekt anhängen, unabhängig davon, was dieses Objekt sonst noch tut. Das ist genau der Grund, warum sie für Dinge wie Delegate- und Datenquellenprotokolle verwendet werden: Selbst wenn Sie diese Dinge meistens an ein ViewController
anhängen, ist das zugrunde liegende Objekt nicht relevant, so dass Sie flexibel bei der Implementierung sein können.
In Swift gibt es viel mehr zu verwenden als nur die Grundlagen: Sie sind außergewöhnlich leistungsfähig, weil sie an verschiedene Code-Konstrukte angehängt werden können: Klassen, Strukturen und Enums. Dies ermöglicht es Ihnen, sich zuerst dem Programmierprotokoll zu nähern. Es gibt ein großartiges Video zu diesem Ansatz von WWDC letztes Jahr , aber es hilft, einige Zeit damit verbracht zu haben, es zu versuchen Einige verschiedene Objekte strukturieren sich zuerst, um ein Gefühl für die Probleme zu bekommen.
Bei Protokollen kann eine Klasse / Struktur als verschiedene Dinge verwendet werden. Zum Beispiel entspricht die String
struct soooo vielen Protokollen!
Dies bedeutet, dass String
als 11 verschiedene Dinge verwendet werden können! Wenn eine Methode eines der obigen Protokolle als Parameter benötigt, können Sie eine Zeichenfolge übergeben.
"Aber ich kann einfach eine Gottklasse erstellen, die alle Methoden hat, die die Protokolle haben!" du hast gestritten. Denken Sie daran, dass Sie in Swift nur von einer einzigen Klasse erben können, und die Mehrfachvererbung ist nur so gefährlich, dass sie Ihren Code super komplex machen kann.
Außerdem können Sie mit Protokollen Ihr benutzerdefiniertes Verhalten der Klasse definieren. String
s hashcode
Methode ist nicht identisch mit der von Int
. Aber sie sind beide mit dieser Funktion kompatibel:
Das ist das wahre Wunder der Protokolle.
Nicht zuletzt machen Protokolle Sinn . Protokolle repräsentieren eine Beziehung "ist eine Art von" oder "kann als verwendet werden". Wenn X als Y verwendet werden kann, macht es Sinn, dass X dem Protokoll Y entspricht, richtig?