Factory Method Pattern

Intención y problema

Factory Method permite variar qué producto concreto se obtiene mediante una operación de creación que el Creator define y un ConcreteCreator redefine o especializa. La formulación GoF se asocia a esa colaboración basada en extensión; el material de CMU la distingue de static factories.

El problema aparece cuando una operación anfitriona necesita cierta capacidad, pero diferentes variantes deben proporcionar implementaciones concretas sin cambiar esa operación. El consumidor principal conoce Product; la decisión de construcción queda en el punto de extensión.

No significa que todo método llamado Create implemente el patrón. El término factory tiene usos modernos más amplios: una función, una operación estática o un objeto selector pueden crear productos útiles sin la relación Creator/ConcreteCreator propia de la formulación clásica.

Fuerzas y estructura

Creator
    → factoryMethod() → Product
 
ConcreteCreator specializes Creator
    → factoryMethod() → ConcreteProduct
                            implements Product

Product define el contrato necesario. ConcreteProduct satisface ese contrato. Creator declara la operación de creación y puede utilizarla en un comportamiento común. ConcreteCreator concreta la decisión, evitando que ese comportamiento nombre cada implementación posible.

El factory method puede ser abstracto o proporcionar una implementación predeterminada que una extensión reemplaza. Diferir la decisión hacia una extensión no implica necesariamente lazy loading, ejecución asíncrona ni creación después de un tiempo de espera.

La tensión es separar variación útil sin introducir una jerarquía solo para reemplazar una línea. También necesitamos definir qué garantiza el producto: estado inicial válido, capacidades disponibles y responsabilidad de liberar recursos si corresponde. La abstracción de creación no elimina esas obligaciones.

Colaboración: un workflow de exportación

En un ejemplo propio, ExportWorkflow valida una solicitud, obtiene un Exporter mediante createExporter y realiza una exportación. El contrato Exporter expresa el trabajo necesario; PdfExportWorkflow y HtmlExportWorkflow seleccionan sus implementaciones respectivas en el punto de creación.

El flujo anfitrión conserva la validación y el tratamiento del resultado. Agregar una variante permite elegir otro producto sin copiar ese flujo. La elección del ConcreteCreator ocurre en otro lugar, por ejemplo al configurar el caso de uso: no desaparece del sistema por quedar fuera del workflow.

Esta separación tiene valor si el protocolo común y sus extensiones son reales. Si la única diferencia es construir un exportador y nunca necesitamos especializar el anfitrión, suministrar una función de creación puede resultar más claro que crear dos subclases.

Además, el workflow debe establecer si obtiene un producto por ejecución, comparte uno reutilizable o administra un recurso con duración limitada. La firma createExporter no informa por sí sola esas decisiones. Un consumidor que supone una instancia nueva puede fallar si recibe una compartida.

Consecuencias y extensión

El comportamiento principal puede depender de Product y dejar que una extensión elija la implementación. Eso reduce referencias a tipos concretos en el protocolo común y concentra una decisión de creación en un lugar reconocible.

El costo incluye más participantes y posible coupling con el protocolo de herencia. Una extensión debe conocer cuándo se invoca el método y qué puede devolver. Cambiar esas expectativas puede romper implementaciones aunque el nombre y el tipo de retorno se conserven.

Agregar un producto compatible puede requerir otro ConcreteCreator, o modificar una extensión existente. Si el producto necesita pasos exclusivos que el anfitrión nunca contempló, el punto de creación no basta: hay que revisar el contrato común. Factory Method no promete extensión ilimitada.

Las pruebas deben observar el workflow con productos válidos y fallidos, además de la decisión de cada extensión. Comprobar únicamente que retorna la clase esperada no demuestra que el nuevo producto satisfaga el comportamiento y lifecycle requeridos.

Constructor y vocabulario de factory

Un constructor directo comunica bien creación simple de un tipo conocido. Una factory, en sentido amplio, puede aportar un nombre significativo, una política de selección, retorno de implementaciones distintas, reutilización o encapsulación del ensamblaje. Esas ventajas no prueban automáticamente GoF Factory Method.

Money.FromDecimal y User.Register pueden ser static factory methods: alternativas nombradas a construir un valor o entidad. No necesitan un Creator con una operación redefinida por ConcreteCreator. Que una operación devuelva un objeto tampoco demuestra que tenga un protocolo de extensión.

Una Simple Factory puede ser una función u objeto que selecciona productos, como PaymentProviderFactory.Create(type). Es terminología habitual para esa organización; no constituye uno de los 23 patrones del catálogo GoF. Puede ser suficiente si la selección explícita ya concentra la variación necesaria.

MecanismoQué expresaQué no demuestra
ConstructorConstrucción directa de un tipo.Variación delegada a un Creator.
Static factoryCreación nombrada mediante una operación estática.Override del punto de creación.
Simple FactorySelección y creación en una función u objeto dedicado.Colaboración GoF basada en extensión del Creator.
GoF Factory MethodCreación especializada mediante el contrato del Creator.Que toda construcción necesite herencia.

La industria usa factory method con sentidos distintos. Al leer una fuente o revisar código, recuperar su definición antes de comparar. Un método llamado Create no identifica el patrón; importan intención, colaboración y consecuencias.

Factory Method y Dependency Injection

Dependency Injection proporciona dependencias desde fuera del consumidor; Factory Method organiza un punto de creación variable. La distinción entre suministro externo y búsqueda explícita de servicios se desarrolla en Fowler: Dependency Injection.

Un DI container puede construir un Exporter y suministrarlo al workflow, pero eso no convierte al container en Factory Method. También puede suministrar una factory cuando el consumidor necesita crear productos con datos que solo conoce durante cada operación.

Usar DI no elimina toda necesidad de factories. A la inversa, una factory que consulta un registro global en cualquier momento puede introducir un Service Locator oculto. Conviene hacer visibles sus dependencias y la política que necesita, sin convertir esta comparación en un artículo sobre containers.

Abstract Factory y Builder

abstract-factory-pattern ofrece creación de productos relacionados como una familia compatible. Factory Method se concentra en la variación de una operación de creación. Una Abstract Factory puede implementar sus operaciones mediante Factory Methods; no son necesariamente alternativas excluyentes.

builder-pattern organiza una construcción progresiva y separa proceso de representación. Factory Method puede elegir el producto o el builder apropiado, pero no describe por sí solo sus etapas. Si la presión principal es ensamblar un objeto complejo, cambiar quién lo instancia puede no resolverla.

creational-patterns compara estas preguntas sin reducirlas a evitar new. Primero identificar si varía una decisión de creación, una familia conjunta o un proceso de ensamblaje; después evaluar una solución concreta.

Alternativas en lenguajes modernos

Una función, closure, delegate, supplier, constructor reference o higher-order function puede representar creación variable sin subclassing. Por ejemplo, createPaymentProvider = createStripeProvider permite proporcionar una decisión concreta donde el consumidor espera una capacidad de creación.

Esa alternativa puede resolver el mismo problema práctico con menos participantes. Conviene describirla como creación parametrizada por una función, en lugar de afirmar que cualquier callback reproduce toda la colaboración GoF. El problema permanece aunque el lenguaje cambie su expresión.

La elección depende del contexto: un framework con extensiones de Creator ya establecidas puede justificar el hook; una operación local con una sola variación puede preferir composición. El mecanismo debe comunicar qué puede variar y preservar sus garantías.

Aplicabilidad y mal uso

Factory Method es candidato cuando existe un anfitrión extensible que necesita productos compatibles y el punto de creación concentra variación real. pattern-selection ayuda a contrastarlo con un constructor, una Simple Factory o una función suministrada.

No suele aportar al envolver cada constructor trivial, añadir herencia solo para cambiar una línea o crear extensiones para variantes hipotéticas. Tampoco es una razón para esconder grandes reglas de negocio dentro de createExporter.

Los malos usos incluyen llamar Factory Method a cualquier Create, hacer consultas globales invisibles y permitir productos con lifecycle inesperado. Revisar qué sabe el Creator, quién elige su extensión y qué contrato conserva el producto permite juzgar el patrón por su colaboración, sin depender del nombre del método.