El Factory Method define una interfaz para crear un objeto, pero deja que las subclases decidan qué clase instanciar. Delega en las subclases la creación del objeto.
La necesidad de crear objetos sin especificar la clase del objeto que se va a crear. En un ejemplo del ámbito de la cocina: debemos preparar un plato, pero no sabemos si será una sopa, una ensalada o un postre hasta el último momento.
La solución que ofrece el Factory Method es:
Como resultado, el cliente puede trabajar con interfaces o clases abstractas sin tener que conocer las clases concretas.
Es como tener un método general para “preparar un plato”, pero las subrecetas (sopas, ensaladas, postres) son las que determinan los ingredientes y procedimientos específicos.
Este patrón es recomendable cuando:
Flexibilidad: al delegar la creación de objetos a las subclases.
Desacoplamiento: separa el código de construcción del código propio del producto
Complejidad: requiere la creación de múltiples subclases, y puede llevar a mayor tiempo de desarrollo
Pensemos en un sistema de gestión de documentos.
En este sistema los usuarios pueden crear diferentes tipos de documentos… como documentos de texto, planilas de cálculo (spreadsheets), presentaciones, etc.
Cada tipo de documento tiene características y comportamientos propios, pero comparten funcionalidades básicas… como abrir, cerrar y guardar.
Los usuarios deben poder crear un documento, pero el tipo exacto de documento dependerá de la elección del usuario.
Por lo tanto, necesitamos una manera de crear tipos de documentos sin especificar las clases concretas de los documentos, hasta el momento de ejecución.
Implementamos un Factory Method que permite a las subclases decidir qué clase de documento instanciar.
Por un lado tenemos los documentos, bajo la clase abstracta o interfaz Document. Una clase que hereda o implementa Document para cada tipo (Text, Spreadsheet), cada una implementa su propio comportamiento para cada acción.
Para la creación de objetos, definimos la clase abstracta DocumentCreator con el método abstracto createDocument().
Para cada tipo de documento (Text, Spreedsheet) creamos una subclase que implementa createDocument(). Cada una va a devolver una instancia del documento correspondiente.
DocumentManager actuaría como el cliente, que crea el documento que quiere.
Codificamos en Java lo que preparamos en el diagrama.
Definimos la clase abstracta Document con las operaciones comunes a todos los documentos, abrir, cerrar y guardar:
abstract class Document {
public abstract void open();
public abstract void close();
public abstract void save();
}
Implementamos subclases concretas para cada documento, cada una con su propio comportamiento:
class TextDocument extends Document {
@Override
public void open() {
/* Implementación específica para abrir documentos de texto */
}
@Override
public void close() {
/* Implementación específica para cerrar documentos de texto */
}
@Override
public void save() {
/* Implementación específica para guardar documentos de texto */
}
}
class SpreadsheetDocument extends Document {
@Override
public void open() {
/* Implementación específica para abrir hojas de cálculo */
}
@Override
public void close() {
/* Implementación específica para cerrar hojas de cálculo */
}
@Override
public void save() {
/* Implementación específica para guardar hojas de cálculo */
}
}
Definimos la clase abstracta DocumentCreator con el método createDocument():
abstract class DocumentCreator {
public abstract Document createDocument();
}
Implementamos DocumentCreator para cada tipo de documento, cada una devuelve una instancia del documento que corresponde:
public class TextDocumentCreator extends DocumentCreator {
@Override
public Document createDocument() {
return new TextDocument();
}
}
public class SpreadsheetDocumentCreator extends DocumentCreator {
@Override
public Document createDocument() {
return new SpreadsheetDocument();
}
}
Finalmente, en el código cliente, creamos instancias mediante estos creators:
public class DocumentManager {
public static void main(String[] args) {
DocumentCreator creator = new TextDocumentCreator();
// Cambiar a SpreadsheetDocumentCreator para crear una hoja de cálculo
Document doc = creator.createDocument();
doc.open();
doc.save();
doc.close();
}
}
Los participantes que vimos antes son: Creador, CreadorConcreto, Producto, ProductoConcreto. Cliente:
En este ejemplo, DocumentManager (el cliente) no necesita saber qué tipo de documento está creando, simplemente utiliza el DocumentCreator correspondiente.
Este enfoque mantiene el código flexible y extensible, permitiendo agregar fácilmente nuevos tipos de documentos sin modificar el código existente.
Factory Method crea un solo objeto mientras que Abstract Factory crea una familia de objetos relacionados o dependientes.
Builder se centra en construir objetos complejos paso a paso, a diferencia del Factory Method, que crea un objeto en un solo paso. Builder también da control sobre el proceso de construcción.
Prototype se basa en la clonación de objetos existentes, No necesita heredar de un Creador, pero suele requerir una operación inicializar en la clase Producto.
Existe un patrón conocido como Factory (o Simple Factory) que no es el Factory Method y muchas veces se lo confunde. Factory es un concepto más simple, no requiere de herencia, solo se centra en encapsular la creación de objetos. No es un patrón oficial de Gang of Four, pero es algo muy usado en el ambiente por su simplicidad.
Los métodos de fabricación muchas veces son invocados desde el interior de un Template Method.