Principes SOLID en Swift : théorie et pratique pour iOS/macOS

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : Pourquoi SOLID est important pour un développeur Swift



Le développement pour iOS et macOS nécessite non seulement la connaissance des frameworks, mais aussi la capacité de créer une architecture maintenable, testable et évolutive. C'est là que les principes SOLID viennent à la rescousse — cinq règles fondamentales de conception orientée objet, formulées par Robert Martin (Uncle Bob).



Dans cet article, nous allons décortiquer chacun des cinq principes SOLID avec des exemples de code en Swift, montrer les antipatterns typiques et proposer des solutions correctes. Après avoir lu cet article, vous serez capable d'écrire un code facile à maintenir, à étendre et à tester.



1. Single Responsibility Principle (SRP) — Principe de responsabilité unique



Énoncé : Une classe ne doit avoir qu'une seule raison de changer. Autrement dit, chaque module est responsable d'exactement une fonctionnalité.



Problème : La classe monstre



Considérons un exemple typique du développement iOS — la classe UserManager, qui en fait trop :



class UserManager {    func fetchUser() -> User { /* requête réseau */ }    func saveUserToDatabase(_ user: User) { /* travail avec CoreData */ }    func formatUserDisplayName(_ user: User) -> String { /* formatage */ }    func sendWelcomeEmail(to user: User) { /* envoi d'email */ }}


Ici, la classe est responsable à la fois des requêtes réseau, de la base de données, du formatage UI et de l'envoi d'emails. La modification de l'un de ces aspects entraînera une modification de UserManager.



Solution : Séparation des responsabilités



class UserNetworkService {    func fetchUser() -> User { /* uniquement la requête réseau */ }}

class UserDatabaseService { func saveUser(_ user: User) { /* uniquement le travail avec la BD */ }}

class UserFormatter { func displayName(for user: User) -> String { /* uniquement le formatage */ }}

class EmailService { func sendWelcome(to user: User) { /* uniquement l'envoi d'email */ }}


Maintenant, chaque classe a une responsabilité claire et unique, ce qui simplifie les tests et la modification.



2. Open/Closed Principle (OCP) — Principe d'ouverture/fermeture



Énoncé : Les entités logicielles doivent être ouvertes à l'extension, mais fermées à la modification.



Problème : Logique rigide



Supposons que nous ayons une classe pour calculer les remises :



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    }}


Lors de l'ajout d'un nouveau type de client (gold), il faudra modifier la classe existante, ce qui viole l'OCP.



Solution : Protocoles et polymorphisme



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) }}


Maintenant, pour ajouter une nouvelle remise, il suffit de créer une nouvelle structure implémentant le protocole DiscountStrategy, sans modifier DiscountCalculator.



3. Liskov Substitution Principle (LSP) — Principe de substitution de Barbara Liskov



Énoncé :

Blogs

Book Recommendations