Patrones de diseño iOS: Factory, Builder, Coordinator y Strategy
Qué problema resuelve cada patrón, ejemplos Swift y cuándo evita o añade complejidad.
Empieza por el problema, no por el nombre
Un patrón es una solución recurrente con compromisos. Factory construye objetos; Builder compone configuraciones; Coordinator gestiona navegación; Strategy sustituye un comportamiento; Singleton ofrece una instancia compartida. Usarlo sin necesidad añade complejidad.
Factory — construir y ensamblar
struct AccountsViewModel {
let repository: any AccountsRepository
}
protocol AccountsRepository { }
struct LiveRepository: AccountsRepository { }
enum AccountsFactory {
static func makeLiveViewModel() -> AccountsViewModel {
AccountsViewModel(repository: LiveRepository())
}
}Trade-off: una factoría evita repetir construcción compleja, pero no necesita jerarquías de clases para cada objeto.
Builder — configuración progresiva
import Foundation
struct RequestConfiguration {
let baseURL: URL
let timeout: TimeInterval
let cacheEnabled: Bool
}
struct RequestBuilder {
let baseURL: URL
var timeout: TimeInterval = 30
var cacheEnabled = false
func build() -> RequestConfiguration {
RequestConfiguration(baseURL: baseURL, timeout: timeout,
cacheEnabled: cacheEnabled)
}
}Trade-off: evita el Builder cuando un inicializador con pocos argumentos expresa mejor el objeto.
Coordinator — navegación
import SwiftUI
@MainActor @Observable
final class AppCoordinator {
var path: [Route] = []
enum Route: Hashable { case account(String), settings }
func showAccount(id: String) { path.append(.account(id)) }
func goBack() { if !path.isEmpty { path.removeLast() } }
}
// Usa NavigationStack(path:) y navigationDestination(for:).Trade-off: útil con deep links y flujos grandes; innecesario en pantallas simples.
Strategy — comportamiento intercambiable
import Foundation
protocol FeeStrategy {
func fee(for amount: Decimal) -> Decimal
}
struct FlatFee: FeeStrategy {
func fee(for amount: Decimal) -> Decimal { 2 }
}
struct PercentageFee: FeeStrategy {
func fee(for amount: Decimal) -> Decimal { amount * 0.02 }
}
struct Checkout {
let fees: any FeeStrategy
func total(_ amount: Decimal) -> Decimal {
amount + fees.fee(for: amount)
}
}Singleton no sustituye DI
Una instancia global puede ser válida para infraestructura inmutable de proceso, pero si guarda estado mutable compartido esconde dependencias y puede acoplar pruebas.
| Patrón | Pregunta | Evitar si |
|---|---|---|
| Factory | ¿Quién construye? | Inicializador trivial |
| Builder | ¿Cómo componer opciones? | Configuración pequeña |
| Coordinator | ¿Quién decide la navegación? | Vista sencilla |
| Strategy | ¿Cómo intercambiar algoritmos? | Solo hay una variante |
| Singleton | ¿Se necesita una instancia global? | Oculta estado compartido |
Notas personales de estudio. Verifica los matices según la versión de Swift/iOS y la documentación oficial.