Problema
Crear familias de objetos relacionados sin especificar sus clases concretas, garantizando que los productos de una familia sean compatibles entre sí.
Propósito
Proporcionar una interfaz para crear familias de objetos relacionados o dependientes sin especificar sus clases concretas. Asegura que los productos creados por una factory sean compatibles entre sí.
Casos de uso comunes
- UI components para diferentes sistemas operativos
- Drivers de base de datos con diferentes proveedores
- Temas visuales (dark/light mode)
- Formatos de exportación (PDF, Excel, Word)
- Protocolos de comunicación (HTTP, HTTPS, FTP)
¿Quién es quién en Abstract Factory?
Actor | Lo que realmente es | Ejemplo | Analogía |
---|---|---|---|
AbstractFactory | Interfaz de fábrica que define createProductX() |
UIFactory - define qué componentes crear |
“Fábrica de muebles” (interfaz) |
ConcreteFactory | Fábricas que crean productos de UNA familia | WindowsFactory , MacFactory |
Fábrica de muebles modernos vs clásicos |
AbstractProduct | Interfaces de productos | Button , Checkbox - definen qué pueden hacer |
“Silla”, “Mesa” (tipos de muebles) |
ConcreteProduct | Implementaciones concretas de productos | WindowsButton , MacButton |
Silla moderna vs Silla clásica |
Client | Usa productos, solo conoce interfaces abstractas | Application - no sabe si usa Windows o Mac |
Decorador (quiere muebles que combinen) |
Diagrama
classDiagram
namespace AbstractFactoryPattern {
class Client {
+main()
}
class AbstractFactory {
<<interface>>
+createProductA() AbstractProductA
+createProductB() AbstractProductB
}
class ConcreteFactory1 {
+createProductA() ProductA1
+createProductB() ProductB1
}
class ConcreteFactory2 {
+createProductA() ProductA2
+createProductB() ProductB2
}
class AbstractProductA {
<<interface>>
+operationA()
}
class AbstractProductB {
<<interface>>
+operationB()
}
class ProductA1 {
+operationA()
}
class ProductA2 {
+operationA()
}
class ProductB1 {
+operationB()
}
class ProductB2 {
+operationB()
}
}
Client --> AbstractFactory
Client --> AbstractProductA
Client --> AbstractProductB
AbstractFactory <|.. ConcreteFactory1
AbstractFactory <|.. ConcreteFactory2
AbstractProductA <|.. ProductA1
AbstractProductA <|.. ProductA2
AbstractProductB <|.. ProductB1
AbstractProductB <|.. ProductB2
ConcreteFactory1 --> ProductA1
ConcreteFactory1 --> ProductB1
ConcreteFactory2 --> ProductA2
ConcreteFactory2 --> ProductB2
Ejemplo práctico
classDiagram
namespace UIExample {
class Application {
+main()
}
class UIFactory {
<<interface>>
+createButton() Button
+createCheckbox() Checkbox
+createTextInput() TextInput
}
class WindowsFactory {
+createButton() WindowsButton
+createCheckbox() WindowsCheckbox
+createTextInput() WindowsTextInput
}
class MacFactory {
+createButton() MacButton
+createCheckbox() MacCheckbox
+createTextInput() MacTextInput
}
class Button {
<<interface>>
+render()
+onClick()
}
class Checkbox {
<<interface>>
+render()
+toggle()
}
class TextInput {
<<interface>>
+render()
+setValue(text)
}
class WindowsButton {
+render()
+onClick()
}
class MacButton {
+render()
+onClick()
}
class WindowsCheckbox {
+render()
+toggle()
}
class MacCheckbox {
+render()
+toggle()
}
class WindowsTextInput {
+render()
+setValue(text)
}
class MacTextInput {
+render()
+setValue(text)
}
}
Application --> UIFactory
Application --> Button
Application --> Checkbox
Application --> TextInput
UIFactory <|.. WindowsFactory
UIFactory <|.. MacFactory
Button <|.. WindowsButton
Button <|.. MacButton
Checkbox <|.. WindowsCheckbox
Checkbox <|.. MacCheckbox
TextInput <|.. WindowsTextInput
TextInput <|.. MacTextInput
WindowsFactory --> WindowsButton
WindowsFactory --> WindowsCheckbox
WindowsFactory --> WindowsTextInput
MacFactory --> MacButton
MacFactory --> MacCheckbox
MacFactory --> MacTextInput
Familias de productos
graph TB
subgraph "Familia 1 (ConcreteFactory1)"
PA1[ProductA1]
PB1[ProductB1]
end
subgraph "Familia 2 (ConcreteFactory2)"
PA2[ProductA2]
PB2[ProductB2]
end
CF1[ConcreteFactory1] --> PA1
CF1 --> PB1
CF2[ConcreteFactory2] --> PA2
CF2 --> PB2
style PA1 fill:#e1f5fe
style PB1 fill:#e1f5fe
style PA2 fill:#f3e5f5
style PB2 fill:#f3e5f5
Flujo de creación
sequenceDiagram
participant Client
participant WindowsFactory
participant WindowsButton
participant WindowsCheckbox
Note over Client: 1. Cliente instancia factory concreta
Client->>WindowsFactory: new WindowsFactory()
WindowsFactory-->>Client: factory instance
Note over Client: 2. Cliente crea productos usando la factory
Client->>WindowsFactory: createButton()
WindowsFactory->>WindowsButton: new WindowsButton()
WindowsButton-->>WindowsFactory: button instance
WindowsFactory-->>Client: WindowsButton
Client->>WindowsFactory: createCheckbox()
WindowsFactory->>WindowsCheckbox: new WindowsCheckbox()
WindowsCheckbox-->>WindowsFactory: checkbox instance
WindowsFactory-->>Client: WindowsCheckbox
Note over Client: 3. Cliente usa productos polimórficamente
Client->>WindowsButton: render()
Client->>WindowsCheckbox: render()
Note over Client, WindowsCheckbox: Todos los productos son de la misma familia (Windows)
Ventajas
- Consistencia: Garantiza que los productos de una familia sean compatibles
- Flexibilidad: Fácil intercambio entre familias de productos
- Aislamiento: El código cliente no depende de clases concretas
- Extensibilidad: Fácil agregar nuevas familias de productos
Desventajas
- Complejidad: Introduce muchas interfaces y clases
- Extensibilidad: Agregar nuevos productos requiere modificar todas las factories
- Rigidez: Difícil cambiar la interfaz de productos existentes
Cuándo usar
- El sistema debe ser independiente de cómo se crean sus productos
- Necesitas garantizar que productos de una familia se usen juntos
- Quieres proporcionar una biblioteca de productos ocultando sus implementaciones
- Tienes múltiples familias de productos relacionados
Cuándo NO usar
- Solo tienes una familia de productos
- Los productos no están relacionados entre sí
- La complejidad adicional no se justifica
- No necesitas garantizar compatibilidad entre productos
Extensiones prácticas comunes
En implementaciones reales, es común agregar un Factory Provider (Simple Factory o Factory Method) para decidir qué factory concreta usar:
// Patrón puro (cliente decide)
const factory = new WindowsFactory();
// Extensión práctica (provider decide)
const factory = FactoryProvider.getFactory(platform);
Esto no es parte del patrón Abstract Factory, sino un patrón complementario que resuelve el problema de selección dinámica.