Principios SOLID en Swift: teoría y práctica para iOS/macOS

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: Por qué SOLID es importante para un desarrollador de Swift



El desarrollo para iOS y macOS requiere no solo conocimiento de frameworks, sino también la capacidad de crear una arquitectura mantenible, testeable y escalable. Aquí es donde entran en juego los principios SOLID — cinco reglas fundamentales del diseño orientado a objetos, formuladas por Robert Martin (Tío Bob).



En este artículo, analizaremos cada uno de los cinco principios SOLID con ejemplos de código en Swift, mostraremos antipatrones típicos y propondremos soluciones correctas. Después de leerlo, podrás escribir código que sea fácil de mantener, extender y probar.



1. Single Responsibility Principle (SRP) — Principio de responsabilidad única



Enunciado: Una clase debe tener una sola razón para cambiar. En otras palabras, cada módulo es responsable de exactamente una funcionalidad.



Problema: La clase monstruo



Consideremos un ejemplo típico del desarrollo iOS: la clase UserManager, que hace demasiado:



class UserManager {    func fetchUser() -> User { /* solicitud de red */ }    func saveUserToDatabase(_ user: User) { /* trabajo con CoreData */ }    func formatUserDisplayName(_ user: User) -> String { /* formateo */ }    func sendWelcomeEmail(to user: User) { /* envío de email */ }}


Aquí la clase es responsable de solicitudes de red, base de datos, formateo de UI y envío de correos. Cambiar cualquiera de estos aspectos implicaría modificar UserManager.



Solución: Separación de responsabilidades



class UserNetworkService {    func fetchUser() -> User { /* solo solicitud de red */ }}

class UserDatabaseService { func saveUser(_ user: User) { /* solo trabajo con BD */ }}

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

class EmailService { func sendWelcome(to user: User) { /* solo envío de email */ }}


Ahora cada clase tiene una responsabilidad clara, lo que simplifica las pruebas y la modificación.



2. Open/Closed Principle (OCP) — Principio de abierto/cerrado



Enunciado: Las entidades de software deben estar abiertas para la extensión, pero cerradas para la modificación.



Problema: Lógica rígida



Supongamos que tenemos una clase para calcular descuentos:



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


Al agregar un nuevo tipo de cliente (gold), habría que modificar la clase existente, lo que viola OCP.



Solución: Protocolos y polimorfismo



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


Ahora, para agregar un nuevo descuento, basta con crear una nueva estructura que implemente el protocolo DiscountStrategy, sin modificar DiscountCalculator.



3. Liskov Substitution Principle (LSP) — Principio de sustitución de Barbara Liskov



Enunciado:

Blogs

Book Recommendations