Introducción: Por qué SOLID es la base del código Swift de calidad
Cada desarrollador de iOS/macOS tarde o temprano se enfrenta a una situación en la que el código se convierte en "espagueti": los cambios en un lugar rompen toda la aplicación, y las pruebas se convierten en una pesadilla. Aquí es donde los principios SOLID vienen al rescate: cinco reglas fundamentales de diseño orientado a objetos formuladas por Robert Martin.
En este artículo, analizaremos cada principio SOLID con ejemplos en Swift, mostraremos cómo su violación genera problemas y proporcionaremos soluciones listas para usar. Aprenderás a escribir código que sea fácil de mantener, extender y probar.
1. Single Responsibility Principle (SRP) — Principio de responsabilidad única
Teoría
SRP establece: una clase debe tener una sola razón para cambiar. En otras palabras, cada clase es responsable de exactamente una funcionalidad.
Violación de SRP (código malo)
class UserManager { func createUser(name: String, email: String) { // Creación de usuario saveToDatabase(user: User(name: name, email: email)) sendWelcomeEmail(to: email) logEvent("User created: \\(name)") } private func saveToDatabase(user: User) { /* ... */ } private func sendWelcomeEmail(to email: String) { /* ... */ } private func logEvent(_ message: String) { /* ... */ }}Aquí UserManager es responsable de la creación, guardado, envío de correos y registro: cuatro razones para cambiar.
Corrección (código bueno)
class UserService { private let repository: UserRepository private let emailService: EmailService private let logger: Logger init(repository: UserRepository, emailService: EmailService, logger: Logger) { self.repository = repository self.emailService = emailService self.logger = logger } func createUser(name: String, email: String) { let user = User(name: name, email: email) repository.save(user) emailService.sendWelcome(to: email) logger.log("User created: \\(name)") }}
class UserRepository { func save(_ user: User) { /* solo guardado */ }}class EmailService { func sendWelcome(to email: String) { /* solo envío */ }}class Logger { func log(_ message: String) { /* solo registro */ }}Ahora cada clase tiene una única responsabilidad, lo que facilita las pruebas y el mantenimiento.
2. Open/Closed Principle (OCP) — Principio de abierto/cerrado
Teoría
Las clases deben estar abiertas para la extensión, pero cerradas para la modificación. La nueva funcionalidad se añade mediante herencia o protocolos, sin tocar el código existente.
Violación de OCP
class PaymentProcessor { func process(payment: Any, type: String) { if type == "card" { // Procesamiento de tarjeta } else if type == "paypal" { // Procesamiento de PayPal } // Al añadir un nuevo tipo — modificamos esta clase }}Corrección mediante protocolos
protocol PaymentMethod { func process(amount: Double)}
class CardPayment: PaymentMethod { func process(amount: Double) { /* procesamiento de tarjeta */ }}
class PayPalPayment: PaymentMethod { func process(amount: Double) { /* procesamiento de PayPal */ }}
class PaymentProcessor { func process(payment: PaymentMethod, amount: Double) { payment.process(amount: amount) }}
// Añadimos un nuevo tipo sin modificar el código existenteclass ApplePayPayment: PaymentMethod { func process(amount: Double) { /* procesamiento de Apple Pay */ }}Ahora, para añadir un nuevo método de pago, basta con crear una nueva clase que implemente el protocolo PaymentMethod.