Einleitung: Warum SOLID für Swift-Entwickler wichtig ist
In der Welt der iOS-Entwicklung ist die Erstellung von wartbarem und skalierbarem Code eine zentrale Aufgabe. Die von Robert Martin formulierten SOLID-Prinzipien bleiben der Goldstandard für Softwarearchitektur. Auf fortgeschrittenem Niveau ermöglicht die Beherrschung dieser Prinzipien in Swift nicht nur das Schreiben von sauberem Code, sondern auch den Aufbau flexibler Systeme, die resistent gegen sich ändernde Anforderungen sind. In diesem Artikel analysieren wir jedes Prinzip mit Beispielen in Swift, die auf reale Aufgaben eines iOS/macOS-Entwicklers ausgerichtet sind.
1. Single Responsibility Principle (SRP) in Swift
Theorie: Jede Klasse oder Struktur sollte nur einen Grund für eine Änderung haben. In der Praxis bedeutet dies, dass ein Modul für eine klar definierte Aufgabe verantwortlich ist.
Problem: Überladener ViewController
In UIKit trifft man häufig auf Controller, die gleichzeitig die UI verwalten, Netzwerkanfragen verarbeiten und Daten speichern. Dies verletzt das SRP.
// ❌ Verletzung des SRPclass UserViewController: UIViewController { @IBOutlet weak var tableView: UITableView! private var users: [User] = []
override func viewDidLoad() { super.viewDidLoad() fetchUsers() }
private func fetchUsers() { // Netzwerkanfrage URLSession.shared.dataTask(with: URL(string: "https://api.example.com/users")!) { data, _, _ in // JSON-Parsing // Speichern in Core Data DispatchQueue.main.async { self.tableView.reloadData() } }.resume() }}Lösung: Aufteilung der Verantwortlichkeiten
Die Arbeit mit Daten und dem Netzwerk wird in separate Dienste ausgelagert.
// ✅ SRP: separater Dienst für die Arbeit mit Benutzernprotocol UserServiceProtocol { func fetchUsers(completion: @escaping (Result<[User], Error>) -> Void)}
class UserService: UserServiceProtocol { func fetchUsers(completion: @escaping (Result<[User], Error>) -> Void) { // Logik der Netzwerkanfrage }}
// ✅ SRP: separater Manager für Core Dataclass CoreDataManager { func saveUsers(_ users: [User]) { // Speichern in Core Data }}
// ✅ SRP: ViewController verwaltet nur die UIclass UserViewController: UIViewController { private let userService: UserServiceProtocol private let coreDataManager: CoreDataManager
init(userService: UserServiceProtocol, coreDataManager: CoreDataManager) { self.userService = userService self.coreDataManager = coreDataManager super.init(nibName: nil, bundle: nil) }
override func viewDidLoad() { super.viewDidLoad() loadUsers() }
private func loadUsers() { userService.fetchUsers { [weak self] result in switch result { case .success(let users): self?.coreDataManager.saveUsers(users) self?.updateUI(with: users) case .failure(let error): self?.showError(error) } } }}2. Open/Closed Principle (OCP) und Swift-Protokolle
Theorie: Software-Entitäten (Klassen, Module) sollten für Erweiterungen offen, aber für Modifikationen geschlossen sein. In Swift wird dies durch Protokolle und Vererbung erreicht.
Beispiel: Benachrichtigungssystem
// ❌ Verletzung des OCP: Beim Hinzufügen eines neuen Benachrichtigungstyps muss der switch geändert werdenclass NotificationSender { func send(_ notification: Notification, type: String) { switch type { case "email": // E-Mail senden case "push": // Push senden default: break } }}
// ✅ OCP: Verwendung eines Protokollsprotocol NotificationChannel { func send(_ notification: Notification)}
class EmailChannel: NotificationChannel { func send(_ notification: Notification)