¿Qué son los patrones de diseño y cómo nos ayudan?
Los patrones de diseño son soluciones comunes a problemas habituales en el desarrollo de software. Estos patrones facilitan la reutilización de código, mejoran la comprensión y fomentan buenas prácticas de programación. Hoy exploraremos el patrón de diseño Factory, un patrón creacional que permite crear fábricas de objetos flexibles y escalables.
¿Cómo implementar el patrón Factory en Go?
Para comenzar, crearemos una interfaz básica y las estructuras necesarias implementándolas en lenguaje Go. Veamos el proceso paso a paso:
Crear la interfaz base: Primero, definimos una interfaz, llamada iProduct, que exigirá la implementación de varios métodos esenciales:
type iProduct interface{setStock(stock int)getStock()intsetName(name string)getName()string}
Definir estructuras concretas: Ahora, definimos nuestra estructura Computer y sus métodos para cumplir con la interfaz iProduct.
type Computer struct{ name string stock int}func(c *Computer)setStock(stock int){ c.stock = stock }func(c *Computer)getStock()int{return c.stock }func(c *Computer)setName(name string){ c.name = name }func(c *Computer)getName()string{return c.name }
Composición sobre herencia: Creamos subclases como Laptop y Desktop, usando composición en Go en lugar de herencia.
Ejecutando este código comprobarás que las instancias de Laptop y Desktop son tratadas adecuadamente como instancias de iProduct.
¿Cuáles son los pros y contras del patrón Factory?
El uso del patrón Factory ofrece claridad en la creación de objetos y flexibilidad para añadir nuevos productos. Sin embargo, puede hacer que el código sea más complejo y difícil de leer, especialmente para novatos en programación.
Al enfrentar la complejidad inicial, el patrón Factory se convierte en una herramienta poderosa para arquitecturas de software robustas. ¡Continúa explorando más patrones de diseño para seguir perfeccionando tus habilidades!
Muchas gracias compañero!. Me gustó el cambio que hiciste en el factory para pasar un apuntador de una instancia y el switch con el type para identificar el tipo de implementación de la interfaz. No conocía esa manera. Muy buen aporte
¿Alguien conoce alguna extensión de VS Code o alguna otra cosa para crear automáticamente todas las funciones necesarias para cumplir los requisitos de una interface?
Ahorraría mucho trabajo definir la interfaz y automáticamente tener creada todas las funciones del struct que e indique
Factory
Es un patrón creacional, que nos permite crear una "fabrica" de objetos a partir de una clase base y a su vez va implementar comportamientos polimórficos que permite modificar el comportamiento de las clases heredadas
Aquí dejo mi código con comentarios
package main
import"fmt"// interfaz que cumple la estructura Computertype IProductinterface{setStock(stock int)getStock() int
setName(name string)getName() string
}//Las estructuras son literalmente clasestype Computer struct { name string
stock int
}//metodos de Computer para satisfacer la interfazfunc(c *Computer)setStock(stock int){ c.stock= stock
}func(c *Computer)setName(name string){ c.name= name
}func(c *Computer)getName() string {return c.name}func(c *Computer)getStock() int {return c.stock}// Podríamos decir que la estructura Laptop es hija de la// estructura Computertype Laptop struct {Computer//composition over inheritance}// Constructor para crear una nueva laptopfunc newLaptop()IProduct{return&Laptop{Computer:Computer{name:"Laptop Computer",stock:25,},}}// Estructura hija de Computer, por lo que obtiene todos sus// métodostype Desktop struct {Computer}// Constructor para crear una instancia de la estructura Desktopfunc newDesktop()IProduct{return&Desktop{Computer{name:"Desktop Computer",stock:35,},}}// Creamos función para determinar cuál estructura se debe// instanciarfunc GetComputerFactory(computerType string)(IProduct, error){if computerType =="laptop"{returnnewLaptop(), nil
}elseif computerType =="desktop"{returnnewDesktop(), nil
}return nil, fmt.Errorf("Invalid computer type")}// imprimimos los productos deseados utilizando la interfazfunc printNameAndStoc(p IProduct){ fmt.Printf("PRoduct name: %s, with stock %d\n", p.getName(), p.getStock())}func main(){ laptop,_:=GetComputerFactory("laptop")// instancia de la estructura Laptop desktop,_:=GetComputerFactory("desktop")// instancia de la estructura DesktopprintNameAndStoc(laptop)printNameAndStoc(desktop)}
Esa definición de interfaces con el prefijo I es muy java de tu parte, no se suele ser tan verbosos en go
Quiero agregar, que no es en sí el poder del patrón de diseño, sino el poder del lenguaje en la forma en la que adminte e integra la composición sobre la herencia, de tal forma que la Struct computadora al componer la struct laptop las instancias de laptop pueden implementar el contrato definido en la interfaz a través de Computadora.
Que pasaría si tuvieramos, 100, 200 o 1000 tipos de computadoras? Hay alguna opción que no sea crear sentencias IF por cada tipo de computadora?
Se me viene a la mente agregar la función de instanciación dentro de Iproduct y en el GetComputerFactory solo usar esa función, en vez de recibir de parametro un string, que reciba el struck, como lo hice el compañero "<img height="24" width="24" alt="Jozek Andrzej Hajduk Sánchez" src="https://static.platzi.com/media/avatars/avatars/HajdukSanchez_2b7947cf-0b8d-4d7f-9b87-7f66d8ba822a.jpg" />, con eso evitamos tener que crear una cadena de ifś
package main
import"errors"// Patron Creacional// Interface que define el comportamiento de un productotype IProductinterface{setStocked(stock int)getStocked() int
getName() string
setName(name string)}// Implementacion de la interfaz IProduct para el producto de tipo "Computadora"type Computer struct { name string
stock int
}// Implementando de forma implicita la interfaz IProductfunc(c *Computer)setStocked(stock int){ c.stock= stock
}func(c *Computer)getStocked() int {return c.stock}func(c *Computer)getName() string {return c.name}func(c *Computer)setName(name string){ c.name= name
}// Creando clase base de computadora, por composicion sobre herenciatype Laptop struct {Computer}func NewLaptop()IProduct{return&Laptop{Computer{"Laptop",25}}}type Desktop struct {Computer}func NewDesktop()IProduct{return&Desktop{Computer{"Desktop",35}}}// Creando fabrica de productos: Factory patternfunc GetComputerFactory(computerType string)(IProduct, error){switch computerType {case"Laptop":returnNewLaptop(), nil
case"Desktop":returnNewDesktop(), nil
default:return nil, errors.New("invalid computer type")}}// Trying polymorphismfunc PrintNameAndStock(product IProduct){println("Name:", product.getName(),"Stock:", product.getStocked())}func main(){ laptop,_:=GetComputerFactory("Laptop") desktop,_:=GetComputerFactory("Desktop")PrintNameAndStock(laptop)PrintNameAndStock(desktop)}
El patrón Factory demuestra cómo la programación no solo se trata de hacer que el código funcione, sino de hacerlo mantenible y escalable. Centralizar la creación de objetos permite que el sistema crezca sin volverse caótico, algo que es clave en proyectos reales donde los requisitos cambian constantemente. Aunque al inicio puede parecer más complejo por la abstracción que introduce, a largo plazo reduce el acoplamiento y mejora la organización del código, lo que refleja una mentalidad más arquitectónica que simplemente funcional.