patron-factory-method

Patrón Factory Method: creación mediante subclases

  • 5 min

El Factory Method es un método de creación cuya implementación pueden decidir las subclases. Permite que la lógica de una clase trabaje con un producto abstracto sin conocer la clase concreta que construye.

Parece inofensivo, ¿verdad? Necesitas un objeto, haces new MiObjeto() y listo. Pero, ¿qué pasa cuando tu aplicación crece y de repente ese objeto ya no es suficiente? ¿Qué pasa si necesitas decidir qué objeto crear en tiempo de ejecución basándote en una configuración o en el entorno?

Si tienes tu código plagado de new Camion(), new Coche(), new Bicicleta(), cada vez que quieras añadir un nuevo vehículo tendrás que abrir y modificar todas las clases que los usan. Eso viola el Principio Abierto/Cerrado (OCP) de SOLID.

Aquí es donde el Factory Method (Método Fábrica) viene al rescate.

El Problema: Un sistema de logística

Imagina que estás creando una aplicación para una empresa de logística. Al principio, la empresa solo reparte por carretera, así que creas una clase Camion.

public class Logistica
{
    public void PlanificarEntrega()
    {
        var camion = new Camion(); // ❌ Acoplamiento fuerte
        camion.Entregar();
    }
}
Copied!

Todo funciona bien. Pero la empresa crece y empieza a repartir por mar. Ahora necesitas barcos.

Si sigues con el mismo diseño, tendrás que llenar tu clase Logistica de condicionales feos:

if (tipo == "carretera") {
    transporte = new Camion();
} else if (tipo == "mar") {
    transporte = new Barco();
}
Copied!

Esto es insostenible. Cada vez que añadas un tipo de transporte (avión, tren, dron…), tendrás que modificar la clase Logistica, arriesgándote a romper código que ya funcionaba.

La Solución: Factory Method

El patrón Factory Method sugiere que en lugar de llamar al operador new directamente, invoquemos a un método factoría especial.

La idea clave es:

Definimos una interfaz común para todos los productos (ej: ITransporte).

Creamos una clase creadora (Creator) que declara el método factoría CrearTransporte().

Dejamos que sean las subclases quienes implementen ese método y decidan qué clase concreta instanciar.

:::

Define una interfaz para crear un objeto, pero deja que sean las subclases quienes decidan qué clase instanciar. Permite que una clase delegue la instanciación a sus subclases.

Implementación en C#

Vamos a refactorizar nuestro ejemplo de logística usando el patrón.

Los Productos (Lo que fabricamos)

Primero, definimos qué comportamiento tienen en común todos nuestros transportes.

// Interfaz común
public interface ITransporte
{
    void Entregar();
}

// Productos concretos
public class Camion : ITransporte
{
    public void Entregar()
    {
        Console.WriteLine("📦 Entrega por carretera en una caja.");
    }
}

public class Barco : ITransporte
{
    public void Entregar()
    {
        Console.WriteLine("🚢 Entrega por mar en un contenedor.");
    }
}
Copied!

El Creador (La Fábrica)

La gracia está en esto. La clase Logistica ya no sabe qué transporte está creando. Solo sabe que recibirá “algo” que cumple con ITransporte.

public abstract class Logistica
{
    // EL FACTORY METHOD
    // Es abstracto, forzamos a las subclases a implementarlo
    public abstract ITransporte CrearTransporte();

    // Lógica de negocio
    // Fíjate que esta clase NO sabe si trabaja con camiones o barcos
    public void PlanificarEntrega()
    {
        // Llamamos al factory method para obtener el objeto
        ITransporte transporte = CrearTransporte();
        
        // Usamos el objeto (Polimorfismo puro)
        transporte.Entregar();
    }
}
Copied!

Los Creadores Concretos

Ahora creamos las versiones específicas de la logística para cada medio.

public class LogisticaVial : Logistica
{
    public override ITransporte CrearTransporte()
    {
        return new Camion();
    }
}

public class LogisticaMaritima : Logistica
{
    public override ITransporte CrearTransporte()
    {
        return new Barco();
    }
}
Copied!

¿Cómo se usa esto?

El código cliente (nuestro Program.cs o controlador) trabaja con la clase abstracta Logistica, sin importarle qué implementación concreta está usando.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("--- Cliente: Quiero envíos por carretera ---");
        EjecutarCliente(new LogisticaVial());

        Console.WriteLine("\n--- Cliente: Quiero envíos por mar ---");
        EjecutarCliente(new LogisticaMaritima());
    }

    // El cliente trabaja con la abstracción 'Logistica'
    static void EjecutarCliente(Logistica logistica)
    {
        logistica.PlanificarEntrega();
    }
}
Copied!

Salida:

--- Cliente: Quiero envíos por carretera ---
📦 Entrega por carretera en una caja.

--- Cliente: Quiero envíos por mar ---
🚢 Entrega por mar en un contenedor.
Copied!

¿Qué hemos ganado?

Fíjate en la potencia de esto. Si mañana queremos añadir Logística Aérea:

  1. Creamos una clase Avion que implemente ITransporte.
  2. Creamos una clase LogisticaAerea que extienda Logistica y devuelva un new Avion().

No modificamos Logistica ni las otras implementaciones. El punto donde elegimos la logística concreta sí tendrá que conocer la nueva opción, salvo que esa selección se resuelva mediante configuración o registro dinámico.

Cuándo usar Factory Method

  • Cuando no sabes de antemano qué tipos exactos de objetos va a necesitar tu código.
  • Cuando quieres proporcionar a los usuarios de tu librería una forma de extender sus componentes internos.
  • Para ahorrar recursos: A veces el método factoría no crea un objeto nuevo cada vez, sino que recicla uno existente (Pooling) o devuelve una instancia de caché. Al tener el control en un método, puedes meter esa lógica ahí sin que el cliente se entere.

El Factory Method es el “padre” de los patrones creacionales. Nos enseña a dejar de depender de las clases concretas y empezar a depender de abstracciones.