Patrón Abstract Factory
Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 88 de 107.
El patrón Abstract Factory extiende el patrón Factory mediante la creación de familias de objetos relacionados sin especificar sus tipos concretos. En lugar de una única función de fábrica, se define una interfaz de fábrica que implementan múltiples fábricas concretas, cada una de las cuales produce un conjunto consistente de objetos relacionados.
Considera un kit de herramientas de interfaz de usuario (UI toolkit) que necesita crear botones y casillas de verificación. Los diferentes sistemas operativos requieren implementaciones distintas, pero los componentes deben funcionar juntos de manera consistente:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}
// Interfaz de fábrica abstracta
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}Cada fábrica concreta produce una familia de componentes relacionados:
// Familia Windows
type WindowsButton struct{}
func (w WindowsButton) Render() string { return "Windows Button" }
type WindowsCheckbox struct{}
func (w WindowsCheckbox) Check() string { return "Windows Checkbox" }
type WindowsFactory struct{}
func (w WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (w WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }
// Familia Mac
type MacButton struct{}
func (m MacButton) Render() string { return "Mac Button" }
type MacCheckbox struct{}
func (m MacCheckbox) Check() string { return "Mac Checkbox" }
type MacFactory struct{}
func (m MacFactory) CreateButton() Button { return MacButton{} }
func (m MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }El código cliente trabaja con la interfaz de la fábrica, manteniéndose independiente de las implementaciones concretas:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}Usa Abstract Factory cuando tu sistema necesite trabajar con múltiples familias de productos relacionados y quieras asegurar que los componentes de la misma familia se utilicen juntos. Es más complejo que un Factory simple, así que recurre a él solo cuando realmente necesites gestionar familias de productos.
Desafío
Fácil¡Vamos a construir un sistema de tienda de muebles utilizando el patrón Abstract Factory! Crearás fábricas que producen familias de artículos de mobiliario relacionados; cada fábrica produce un estilo consistente (Moderno o Vintage), asegurando que las sillas y mesas de la misma fábrica combinen estéticamente.
Organizarás tu código en tres archivos:
furniture.go: Define tus interfaces de producto y tipos de muebles concretos.Crea dos interfaces de producto:
Chaircon un métodoSitOn() stringTablecon un métodoPlaceItems() string
Implementa dos familias de muebles:
- Familia Modern:
ModernChairdevuelve"Sitting on a sleek modern chair"yModernTabledevuelve"Placing items on a glass modern table" - Familia Vintage:
VintageChairdevuelve"Sitting on an ornate vintage chair"yVintageTabledevuelve"Placing items on a wooden vintage table"
factory.go: Define tu interfaz de fábrica abstracta y fábricas concretas.Crea una interfaz
FurnitureFactorycon dos métodos:CreateChair() ChairCreateTable() Table
Implementa dos fábricas concretas:
ModernFactoryque produceModernChairyModernTableVintageFactoryque produceVintageChairyVintageTable
También crea una función
GetFactory(style string) FurnitureFactoryque devuelva la fábrica adecuada basada en el estilo ("modern"o"vintage"). Por defecto, usaModernFactorypara estilos no reconocidos.main.go: Usa tus fábricas para amueblar habitaciones.Crea una función
FurnishRoom(factory FurnitureFactory) stringque utilice la fábrica para crear una silla y una mesa, y luego devuelva una cadena formateada que muestre ambas acciones en líneas separadas.Lee una entrada de estilo, obtén la fábrica adecuada, amuebla una habitación e imprime el resultado.
Se proporcionarán las siguientes entradas:
- Línea 1: Estilo de mueble (
"modern","vintage", u otro)
Por ejemplo, dado:
modernTu salida debería ser:
Sitting on a sleek modern chair
Placing items on a glass modern tableY dado:
vintageTu salida debería ser:
Sitting on an ornate vintage chair
Placing items on a wooden vintage tableY dado:
rusticTu salida debería ser:
Sitting on a sleek modern chair
Placing items on a glass modern tableObserva cómo la función FurnishRoom funciona con cualquier fábrica a través de la interfaz; no sabe si está creando muebles modernos o vintage. ¡Esto asegura que todos los muebles de una habitación provengan de la misma familia, manteniendo un estilo consistente!
Hoja de referencia
El patrón Abstract Factory crea familias de objetos relacionados sin especificar sus tipos concretos. Define una interfaz de fábrica que implementan múltiples fábricas concretas, cada una produciendo un conjunto consistente de objetos relacionados.
Estructura
Defina interfaces de producto para cada tipo de objeto:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}Cree una interfaz de fábrica abstracta que declare métodos para crear cada tipo de producto:
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}Implementaciones Concretas
Implemente familias de productos concretas donde cada familia tenga un estilo o comportamiento consistente:
// Familia Windows
type WindowsButton struct{}
func (w WindowsButton) Render() string { return "Windows Button" }
type WindowsCheckbox struct{}
func (w WindowsCheckbox) Check() string { return "Windows Checkbox" }
type WindowsFactory struct{}
func (w WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (w WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }
// Familia Mac
type MacButton struct{}
func (m MacButton) Render() string { return "Mac Button" }
type MacCheckbox struct{}
func (m MacCheckbox) Check() string { return "Mac Checkbox" }
type MacFactory struct{}
func (m MacFactory) CreateButton() Button { return MacButton{} }
func (m MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }Código del Cliente
El código del cliente trabaja con la interfaz de la fábrica, permaneciendo independiente de las implementaciones concretas:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}Cuándo usarlo
Use Abstract Factory cuando su sistema necesite trabajar con múltiples familias de productos relacionados y desee asegurarse de que los componentes de la misma familia se utilicen juntos. Es más complejo que una Factory simple, así que úselo solo cuando realmente necesite gestionar familias de productos.
Pruébalo tú mismo
package main
import "fmt"
// FurnishRoom utiliza una fábrica para crear muebles y devuelve el resultado
// TODO: Implementar esta función
// - Usar la fábrica para crear una silla y una mesa
// - Devolver una cadena con el resultado de SitOn() en la primera línea y el de PlaceItems() en la segunda
func FurnishRoom(factory FurnitureFactory) string {
// TODO: Crear silla y mesa usando la fábrica
// TODO: Devolver la cadena formateada con ambas acciones en líneas separadas
return ""
}
func main() {
var style string
fmt.Scanln(&style)
// TODO: Obtener la fábrica adecuada basada en el estilo
// TODO: Amueblar una habitación usando la fábrica
// TODO: Imprimir el resultado
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO en Go
Archivos externosEspacio de trabajo y módulos de GoPaquetes e importacionesNombres exportados vs. no exportadosIntroducción a la POO en GoStructs como clasesDefinición de métodos en structsReceptores de puntero vs. de valorInicialización de structsFunciones constructorasResumen - Calculadora simple4Interfaces
Introducción a las interfacesImplementación implícitaLa interfaz como contratoInterfaz vacía (any)Type AssertionType SwitchComposición de interfacesInterfaces Stringer y ErrorResumen: Calculadora de figuras7Encapsulamiento
Campos exportados vs. no exportadosEncapsulamiento a nivel de paqueteMétodos Getter y SetterOcultamiento de información en GoResumen - Registros de estudiantes10Genéricos (Go 1.18+)
Introducción a los GenéricosParámetros de tipoRestricciones de tipoStructs genéricosSolución alternativa para métodos genéricosResumen - Colección genérica13Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón Abstract FactoryPatrón ObserverPatrón Strategy2Inmersión profunda en Tipos y Structs
Tipos básicos y compuestosDefiniciones de tipos personalizadosEtiquetas de StructsStructs anónimosStructs anidadosValores cero y por defectoRepaso - Libreta de contactos5Composición sobre herencia
Por qué Go no tiene herenciaConceptos básicos de incrustación de structsPromoción de métodosIncrustación de múltiples structsIncrustación vs AgregaciónShadowing de métodos incrustadosResumen - Jerarquía de empleados8Manejo de Errores y OOP
La interfaz errorTipos de errores personalizadosEnvoltura de errores (fmt.Errorf)Errores centinelaerrors.Is() y errors.As()Panic, Defer y RecoverResumen - Analizador de archivos3Punteros y Memoria
Fundamentos de punteros en GoPunteros a StructsPaso por valor vs referenciaLa función new()Garbage Collection en GoResumen - Constructor de Linked List6Polimorfismo en Go
Polimorfismo mediante interfacesDuck Typing en GoReglas de satisfacción de interfacesColecciones polimórficasInyección de dependenciasResumen - Procesador de pagos9Concurrencia y POO
Fundamentos de GoroutinesChannels y comunicaciónChannels con buffer vs. sin bufferSentencia Selectsync.Mutex y sync.RWMutexsync.WaitGroupDiseño de estructuras Thread-SafeResumen - Worker Pool