Protokollkonformität mittels Extensions „nachrüsten“

Auf Basis eines jüngst von mir erstellten Videos zu Let‘s Code POP auf YouTube möchte ich euch in diesem Artikel die Möglichkeit vorstellen, einem Typ „nachträglich“ mittels Extension ein Protokoll zuzuweisen. Ich nutze diese Technik sehr exzessiv, um meinen Code besser zu strukturieren und die grundlegende Implementierung eines Typs von dessen Protokolleigenschaften zu trennen.

Wie funktioniert‘s?

Im Grunde ist diese Technik von Swift gleichermaßen simpel wie mächtig. Ihr könnt jedem verfügbaren Typ außerhalb seiner Deklaration ein Protokoll zuweisen, indem ihr eine Extension dieses Typs erstellt und darüber die Konformität vornehmt.

Ein simples Beispiel dazu zeigt das folgende Listing. Darin wird ein Protokoll namens TextRepresentable mit einer einzigen Eigenschaft namens textualDescription definiert. Es ist für Typen gedacht, die für eine individuelle Instanz eine Beschreibung in Textform zurückliefern können.

Im Anschluss erfolgt die Erstellung einer Structure namens Book zur Abbildung von Büchern. Sie besitzt zwei Eigenschaften: eine Property für den Titel einer Geschichte sowie eine für den Autor.

Die Structure wird in ihrer Deklaration nicht als konform zum TextRepresentable-Protokoll definiert. Um das „nachträglich“ festzulegen, wird im nächsten Schritt eine Extension von Book erstellt und darin die gewünschte Deklaration vorgenommen (inklusive passender Implementierung der textualDescription-Property des TextRepresentable-Protokolls).

protocol TextRepresentable {
    var textualDescription: String { get }
}

struct Book {
    var author: String
    var title: String
}

extension Book: TextRepresentable {
    var textualDescription: String {
        return "'\(title)' von \(author)."
    }
}

let someBook = Book(author: "Thomas Sillmann", title: "Schatten")
print(someBook.textualDescription)
// 'Schatten' von Thomas Sillmann.

Einsatzzweck und Übersichtlichkeit

Womöglich fragt ihr euch, wozu man eine solche Trennung der Protokollzuweisung überhaupt vornehmen sollte? Schließlich hätte man die Structure Book direkt als konform zum TextRepresentable-Protokoll definieren und direkt die Implementierung der textualDescription-Property vornehmen können.

Gerade bei umfangreichen und komplexen Typen mit vielen Eigenschaften und Funktionen kann solch ein Vorgehen aber sehr unübersichtlich werden. Wenn solche Typen dann nämlich noch konform zu mehreren verschiedenen Protokollen sind, durch die diverse zusätzliche Eigenschaften und Funktionen implementiert werden müssen, kann das die Komplexität des eigenen Codes deutlich erhöhen.

Ein häufiges Beispiel, in dem die Aufteilung der Protokollimplementierung in Extensions in meinen Augen voll zur Geltung kommt, ist bei der Arbeit mit Table-Views in der iOS- beziehungsweise tvOS-Entwicklung. Die Methoden des UITableViewDataSource– und UITableViewDelegate-Protokolls lagere ich so in Extensions aus und weiß damit ganz genau, wo ich die entsprechenden Befehle und Implementierungen finde. Gleichzeitig verfügt mein View-Controller nur über jene Eigenschaften und Funktionen, die ihn selbst auch explizit betreffen. Dieser grundlegende Aufbau ist im folgenden Listing skizziert.

class ViewController: UIViewController {
	// Implementierung des View-Controllers.
}

extension ViewController: UITableViewDataSource {
	// Implementierung des Data-Source der Table-View.
}

extension ViewController: UITableViewDelegate {
	// Implementierung des Delegate der Table-View.
}

Protokollkonformität für alle

Ebenfalls enorm spannend ist hierbei, dass sich auf die gezeigte Art und Weise alle verfügbaren Typen um Protokollkonformität erweitern lassen; auch jene, auf deren Implementierung wir gar keinen Zugriff haben. Dazu gehören zum Beispiel die Typen der Swift Standard Library oder die der System-Frameworks von Apple (wie UIKit oder AppKit).

Entsprechend lässt sich so beispielsweise der Typ Int als ebenfalls konform zum vorgestellten TextRepresentable-Protokoll deklarieren, wie das folgende Listing zeigt:

extension Int: TextRepresentable {
    var textualDescription: String {
        return "Eine Ganzzahl mit dem Wert \(self)."
    }
}

let someInt = 19
print(someInt.textualDescription)
// Eine Ganzzahl mit dem Wert 19.

Fazit

Die Möglichkeit, einem Typen mittels Extension ein Protokoll zuzuweisen, bringt in Swift viele Vorteile mit sich. Nicht nur lässt sich damit der eigene Code deutlich besser strukturieren (was bereits für sich genommen ein enormer Gewinn ist!), sondern es ermöglicht es sogar, Typen, auf deren Implementierung wir keinen Zugriff haben, ebenfalls verschiedenste Protokolle zuzuweisen.

Euer Thomas


Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert