Patrón Abstract Factory: Familias de Objetos Relacionados
design-patternscreationalabstract-factory

Intención
Abstract Factory proporciona una interfaz para crear familias de objetos relacionados sin especificar sus clases concretas. Garantiza que los productos creados sean compatibles entre sí.
El Problema
Imagina un sistema de UI que debe soportar múltiples temas. Cada tema tiene botones, inputs y cards con estilos consistentes:
// Sin patrón: lógica de tema dispersa
function createButton(theme: string) {
if (theme === 'light') return new LightButton();
if (theme === 'dark') return new DarkButton();
}
function createInput(theme: string) {
if (theme === 'light') return new LightInput();
if (theme === 'dark') return new DarkInput();
}
// ¿Qué pasa si alguien mezcla un LightButton con DarkInput?La Solución: Abstract Factory
classDiagram
class UIFactory {
<<interface>>
+createButton(): Button
+createInput(): Input
+createCard(): Card
}
class LightThemeFactory {
+createButton(): LightButton
+createInput(): LightInput
+createCard(): LightCard
}
class DarkThemeFactory {
+createButton(): DarkButton
+createInput(): DarkInput
+createCard(): DarkCard
}
UIFactory <|.. LightThemeFactory
UIFactory <|.. DarkThemeFactoryImplementación TypeScript
// Productos abstractos
interface Button {
render(): string;
onClick(handler: () => void): void;
}
interface Input {
render(): string;
getValue(): string;
}
// Fábrica abstracta
interface UIFactory {
createButton(label: string): Button;
createInput(placeholder: string): Input;
}
// Fábrica concreta: Light Theme
class LightThemeFactory implements UIFactory {
createButton(label: string): Button {
return {
render: () => `<button class="bg-white text-gray-900 border">${label}</button>`,
onClick: (handler) => handler(),
};
}
createInput(placeholder: string): Input {
return {
render: () => `<input class="bg-white border-gray-300" placeholder="${placeholder}">`,
getValue: () => '',
};
}
}
// Fábrica concreta: Dark Theme
class DarkThemeFactory implements UIFactory {
createButton(label: string): Button {
return {
render: () => `<button class="bg-gray-800 text-white">${label}</button>`,
onClick: (handler) => handler(),
};
}
createInput(placeholder: string): Input {
return {
render: () => `<input class="bg-gray-900 text-white" placeholder="${placeholder}">`,
getValue: () => '',
};
}
}
// Uso con inyección de dependencias
class LoginForm {
constructor(private factory: UIFactory) {}
render(): string {
const emailInput = this.factory.createInput('Email');
const passwordInput = this.factory.createInput('Password');
const submitButton = this.factory.createButton('Login');
return `
<form>
${emailInput.render()}
${passwordInput.render()}
${submitButton.render()}
</form>
`;
}
}
// El cliente decide qué fábrica usar
const theme = getUserPreference();
const factory = theme === 'dark' ? new DarkThemeFactory() : new LightThemeFactory();
const loginForm = new LoginForm(factory);Cuándo Usar
| Situación | Recomendación |
|---|---|
| Múltiples temas/skins | ✅ Recomendado |
| Soporte multiplataforma | ✅ Recomendado |
| Familias de productos relacionados | ✅ Recomendado |
| Producto único sin variantes | ❌ Usar Factory Method |
Conclusión
Abstract Factory garantiza consistencia al crear familias de objetos relacionados, evitando combinaciones incompatibles.