Patrones de diseño en Swift: de los fundamentos a la práctica
Los patrones de diseño (Design Patterns) son soluciones probadas para problemas típicos de desarrollo. En el mundo de Swift y el ecosistema de Apple (iOS/macOS), su conocimiento se vuelve obligatorio para crear código mantenible, testeable y escalable. En este artículo analizaremos los patrones clave, dividiéndolos en tres categorías principales: creacionales, estructurales y de comportamiento. Cada patrón se ilustrará con un ejemplo funcional en Swift.
Patrones creacionales: Singleton, Factory Method y Builder
Los patrones creacionales se encargan del proceso de creación de objetos, haciéndolo más flexible e independiente del código cliente.
Singleton (Instancia única)
Garantiza que una clase tenga solo una instancia y proporciona un punto de acceso global a ella. En iOS se usa a menudo para gestionar sesiones de red, bases de datos (Core Data) o configuraciones de la aplicación.
class NetworkManager { static let shared = NetworkManager() private init() {} func fetchData(from url: URL) { // Lógica de la solicitud print("Fetching data from \\(url)") }}
// UsoNetworkManager.shared.fetchData(from: URL(string: "https://example.com")!)Factory Method (Método de fábrica)
Define una interfaz para crear un objeto, pero permite que las subclases alteren el tipo de objeto creado. Es ideal para crear componentes de UI según la versión de iOS o la configuración.
protocol Button { func render()}
class iOSButton: Button { func render() { print("Rendering iOS button") }}
class macOSButton: Button { func render() { print("Rendering macOS button") }}
enum Platform { case iOS, macOS }
class ButtonFactory { static func createButton(for platform: Platform) -> Button { switch platform { case .iOS: return iOSButton() case .macOS: return macOSButton() } }}
// Usolet button = ButtonFactory.createButton(for: .iOS)button.render()Builder (Constructor)
Permite construir objetos complejos paso a paso. Se usa ampliamente para configurar solicitudes (URLRequest) o ajustes complejos de UI.
class Pizza { var dough: String = "" var cheese: String = "" var toppings: [String] = []}
class PizzaBuilder { private var pizza = Pizza() func setDough(_ dough: String) -> PizzaBuilder { pizza.dough = dough return self } func setCheese(_ cheese: String) -> PizzaBuilder { pizza.cheese = cheese return self } func addTopping(_ topping: String) -> PizzaBuilder { pizza.toppings.append(topping) return self } func build() -> Pizza { return pizza }}
// Usolet margherita = PizzaBuilder() .setDough("Thin") .setCheese("Mozzarella") .addTopping("Basil") .build()Patrones estructurales: Adapter, Decorator y Facade
Los patrones estructurales simplifican el diseño al definir formas de combinar objetos y clases en estructuras más grandes.
Adapter (Adaptador)
Permite que objetos con interfaces incompatibles trabajen juntos. Un ejemplo clásico es la integración de bibliotecas de terceros o el trabajo con código heredado.
// Protocolo antiguoprotocol OldPaymentSystem { func pay(amount: Double)}
// Protocolo nuevoprotocol NewPaymentSystem { func processPayment(amount: Double, currency: String)}
// Adaptadorclass PaymentAdapter: NewPaymentSystem { private let oldSystem: OldPaymentSystem init(oldSystem: OldPaymentSystem) { self.oldSystem = oldSystem } func processPayment(amount: Double, currency: String) { // Conversión de moneda let convertedAmount = amount * 1.1 oldSy