SOLID-Prinzipien in Swift: Theorie und Praxis für iOS/macOS

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Warum SOLID für Swift-Entwickler wichtig ist



Die Entwicklung für iOS und macOS erfordert nicht nur Kenntnisse über Frameworks, sondern auch die Fähigkeit, eine wartbare, testbare und skalierbare Architektur zu erstellen. Hier kommen die SOLID-Prinzipien ins Spiel – fünf fundamentale Regeln des objektorientierten Designs, formuliert von Robert C. Martin (Uncle Bob).



In diesem Artikel werden wir jedes der fünf SOLID-Prinzipien anhand von Swift-Codebeispielen analysieren, typische Antipatterns aufzeigen und korrekte Lösungen vorschlagen. Nach der Lektüre werden Sie in der Lage sein, Code zu schreiben, der leicht zu warten, zu erweitern und zu testen ist.



1. Single Responsibility Principle (SRP) – Prinzip der einzigen Verantwortung



Formulierung: Eine Klasse sollte nur einen einzigen Grund für eine Änderung haben. Mit anderen Worten, jedes Modul ist für genau eine Funktionalität verantwortlich.



Problem: Die Monster-Klasse



Betrachten wir ein typisches Beispiel aus der iOS-Entwicklung – die Klasse UserManager, die zu viel tut:



class UserManager {    func fetchUser() -> User { /* Netzwerkanfrage */ }    func saveUserToDatabase(_ user: User) { /* Arbeit mit CoreData */ }    func formatUserDisplayName(_ user: User) -> String { /* Formatierung */ }    func sendWelcomeEmail(to user: User) { /* E-Mail-Versand */ }}


Hier ist die Klasse verantwortlich für Netzwerkanfragen, Datenbank, UI-Formatierung und E-Mail-Versand. Eine Änderung an einem dieser Aspekte würde eine Änderung an UserManager erzwingen.



Lösung: Aufteilung der Verantwortlichkeiten



class UserNetworkService {    func fetchUser() -> User { /* nur Netzwerkanfrage */ }}

class UserDatabaseService { func saveUser(_ user: User) { /* nur Datenbankarbeit */ }}

class UserFormatter { func displayName(for user: User) -> String { /* nur Formatierung */ }}

class EmailService { func sendWelcome(to user: User) { /* nur E-Mail-Versand */ }}


Jetzt hat jede Klasse eine klare, einzige Verantwortung, was das Testen und die Modifikation vereinfacht.



2. Open/Closed Principle (OCP) – Prinzip der Offenheit/Geschlossenheit



Formulierung: Software-Entitäten sollten offen für Erweiterung, aber geschlossen für Modifikation sein.



Problem: Starre Logik



Angenommen, wir haben eine Klasse zur Berechnung von Rabatten:



class DiscountCalculator {    func calculateDiscount(for order: Order, customerType: String) -> Double {        if customerType == "regular" {            return order.total * 0.05        } else if customerType == "premium" {            return order.total * 0.1        } else if customerType == "vip" {            return order.total * 0.2        }        return 0    }}


Beim Hinzufügen eines neuen Kundentyps (gold) müsste die bestehende Klasse geändert werden, was OCP verletzt.



Lösung: Protokolle und Polymorphismus



protocol DiscountStrategy {    func discount(for order: Order) -> Double}

struct RegularDiscount: DiscountStrategy { func discount(for order: Order) -> Double { order.total * 0.05 }}

struct PremiumDiscount: DiscountStrategy { func discount(for order: Order) -> Double { order.total * 0.1 }}

struct VIPDiscount: DiscountStrategy { func discount(for order: Order) -> Double { order.total * 0.2 }}

class DiscountCalculator { func calculateDiscount(for order: Order, using strategy: DiscountStrategy) -> Double { return strategy.discount(for: order) }}


Jetzt reicht es, um einen neuen Rabatt hinzuzufügen, eine neue Struktur zu erstellen, die das Protokoll DiscountStrategy implementiert, ohne DiscountCalculator zu ändern.



3. Liskov Substitution Principle (LSP) – Prinzip der Substitution von Barbara Liskov



Formulierung:

Blogs

Buchempfehlungen