Diseño de software

Patrón Factory Method en Swift: por qué usarlo en lugar de init

Las funciones factory de Swift asignan nombres que expresan la intención a la lógica de creación y flexibilizan el tipo de retorno y la política de reutilización. Resumimos cuándo elegir static func en lugar de init y su diferencia con el Factory Method de GoF.

4 min de lectura
Imagen de portada de Patrón Factory Method en Swift: por qué usarlo en lugar de init

Al crear aplicaciones con Swift, acabas escribiendo código para crear objetos decenas de veces al día.

Pero llega un momento en que usar solo init resulta cada vez más limitado.

Muchos empiezan usando init para todo y, cuando el proyecto crece, descubren el patrón Factory Method.

En este artículo explico qué es el patrón Factory Method en Swift y por qué usar una factory en lugar de init, con código real.

El patrón Factory Method en una frase

Empecemos por la respuesta clave.

Un método factory extrae la lógica de creación de objetos a un método independiente fuera de init, le da un nombre y permite elegir libremente el tipo de retorno.

En Swift, normalmente se refiere a un método factory estático creado con static func.

Aquí hablamos de una factory estática que envuelve la creación en un método de tipo. Es distinta del patrón de GoF, en el que una subclase decide el tipo creado; comparamos ambos conceptos en Factory Method vs. Abstract Factory.

init solo funciona dentro de las reglas que define el lenguaje.

No puedes darle otro nombre y siempre debe crear una instancia nueva de su propio tipo.

Los métodos factory eliminan estas limitaciones.

Veamos un ejemplo sencillo.

struct Color {
    let r, g, b: Double
    // Expresar la intención de creación en el nombre
    static func rgb(_ r: Double, _ g: Double, _ b: Double) -> Color {
        Color(r: r, g: g, b: b)
    }
    static func gray(_ v: Double) -> Color {
        Color(r: v, g: v, b: v)
    }
}

Si se llama como Color.gray(0.5), el propio código deja claro que crea el color gris.


¿Por qué usar una factory en lugar de init?

Por mi experiencia, hay cuatro razones principales.

Primero, puedes ponerle un nombre.

Todos los init tienen el mismo nombre, así que solo puedes diferenciarlos por sus parámetros.

Si hay varios constructores que reciben dos valores Double, es fácil confundirse.

Los métodos factory expresan la intención en nombres como from(hex:) y rgb(_:_:_:).

Segundo, no tienes que crear una instancia nueva cada vez.

init siempre devuelve un objeto nuevo, pero una factory puede devolver un valor en caché o un singleton.

Tercero, puedes elegir el tipo de retorno con flexibilidad.

Según la situación, puede devolver un subtipo u otra implementación.

Cuarto, permite expresar con más claridad el manejo de errores.

Con una sola línea de static func, el código se vuelve mucho más legible
Con una sola línea de static func, el código se vuelve mucho más legible

Este es un ejemplo de una factory que usa caché.

final class IconCache {
    private static var store: [String: Icon] = [:]
    // Reutilizar el icono ya creado si tiene el mismo nombre
    static func icon(named name: String) -> Icon {
        if let cached = store[name] { return cached }
        let icon = Icon(name: name)
        store[name] = icon
        return icon
    }
}

Al solicitar el mismo icono varias veces, puedes ahorrar memoria.

Si existe, lo devuelve; si no, lo crea y lo guarda
Si existe, lo devuelve; si no, lo crea y lo guarda

Entonces, ¿ya no hace falta usar init?

No, en absoluto.

Una factory no sustituye a init; es una herramienta que lo envuelve.

Incluso dentro de un método factory, al final llamas a init para crear el objeto.

Para un tipo por valor simple o una lógica de creación obvia, es mejor usar init directamente.

Envolverlo en una factory sin motivo solo alarga el código y dificulta su lectura.

Las factories destacan en situaciones concretas.

  • Cuando hay varias formas de creación y quieres distinguirlas por el nombre
  • Cuando necesitas reutilización mediante caché o pooling
  • Cuando quieres ocultar en un solo lugar un proceso de inicialización complejo
  • Cuando quieres ocultar la implementación devolviendo un tipo de protocolo

Si no se cumple ninguna condición, usa init.

La biblioteca estándar de Swift también hace esta distinción.

Array(repeating:count:) es un init, mientras que propiedades estáticas como UIColor.systemBlue son, en la práctica, primas de las factories.


Criterios prácticos para decidir

He resumido en una tabla los criterios que uso cuando tengo dudas.

Situación Recomendación
Almacenar valores simples init
Varias formas de creación Método factory
Reutilización o caché de instancias Método factory
Ocultar la implementación y devolver un protocolo Método factory
La inicialización termina en una línea init
Cuando dudo, recuerdo esta tabla para elegir
Cuando dudo, recuerdo esta tabla para elegir

En resumen, init trata de «cómo crear», mientras que una factory trata de «qué crear y por qué».

No compiten entre sí; son compañeros con roles diferentes.

Recomiendo empezar con init y pasar a una factory cuando la lógica de creación se vuelva compleja.

Con recordar estos criterios, tu código será mucho más limpio. ¡Feliz programación con Swift!

Seguir leyendo