Factory https://refactoring.guru/es/design-patterns/abstract-factory
Introducción
Cómo aprender Go avanzado: concurrencia, patrones de diseño y net
Concurrencia
Race condition: el problema de depositar y retirar
Sync Mutex: Lock y Unlock
Mutex de lectura y escritura
Sistema de caché sin concurrencia
Sistema de caché con concurrencia
Reutilización de computación intensiva
Patrones de diseño
¿Qué son los patrones de diseño?
Factory
Singleton
Adapter
Observer
Strategy
Net
Escaneador de puertos sin concurrencia
Escaneador de puertos con concurrencia
Netcat
Servidor de chat en Go con net
Terminando el chat
Conclusión
Resumen del curso de Go avanzado
You don't have access to this class
Keep learning! Join and start boosting your career
Design patterns are common solutions to common problems in software development. These patterns facilitate code reuse, improve comprehension and encourage good programming practices. Today we will explore the Factory
design pattern, a creative pattern that allows you to create flexible and scalable object factories.
To begin with, we will create a basic interface and the necessary structures by implementing them in Go language. Let's see the process step by step:
Create the base interface: First, we define an interface, called iProduct
, which will require the implementation of several essential methods:
type iProduct interface { setStock(stock int) getStock() int setName(name string) getName() string}
Define concrete structures: Now, we define our Computer
structure and its methods to comply with the iProduct
interface.
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 }
Composition over inheritance: We create subclasses like Laptop
and Desktop
, using composition in Go instead of inheritance.
type Laptop struct { Computer}
func newLaptop() iProduct { return &Laptop{Computer{name: "Laptop Computer", stock: 25}}}
type Desktop struct { Computer}
func newDesktop() iProduct { return &Desktop{Computer{name: "Desktop Computer", stock: 35}}}
Implement Factory: The next step is to create the getComputerFactory
function that uses the Factory pattern to return the appropriate product.
func getComputerFactory(computerType string) (iProduct, error) { if computerType == "laptop" { return newLaptop(), nil } else if computerType == "desktop" { return newDesktop(), nil } return nil, fmt.Errorf("Invalid computer type")}
The Factory pattern offers multiple benefits:
iProduct
, promoting the use of polymorphism and code reuse.Create a print function to visualize the results and verify the polymorphic behavior:
{ fmt.Printf("Product Name: %s, Stock: %d", p.getName(), p.getStock())}
func main() { laptop, _ := getComputerFactory("laptop") desktop, _ := getComputerFactory("desktop")
printNameAndStock(laptop) printNameAndStock(laptop) printNameAndStock(desktop)}
Running this code will verify that the Laptop
and Desktop
instances are properly treated as iProduct
instances.
Using the Factory pattern offers clarity in object creation and flexibility in adding new products. However, it can make code more complex and difficult to read, especially for programming novices.
By addressing the initial complexity, the Factory pattern becomes a powerful tool for robust software architectures. Continue exploring more design patterns to further hone your skills!
Contributions 8
Questions 2
Acá unas modificaciones que agregué al Código, espero les sirva:
// ToString function
func (c *Computer) String() string {
s := fmt.Sprintf("Product: %s, with Stock: %d", c.name, c.stock)
return s
}
// This is the factory
func ComputerFactory(computerType Product) (Product, error) {
switch computerType.(type) {
case *Desktop:
return NewDesktop(), nil
case *Laptop:
return NewLaptop(), nil
default:
return nil, fmt.Errorf("Unknown computer type: %T", computerType)
}
}
La función main se vería algo así al hacer el proceso de creación:
func main() {
laptop, _ := ComputerFactory(&Desktop{})
desktop, _ := ComputerFactory(&Laptop{})
fmt.Println(laptop)
fmt.Println(desktop)
}
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
package main
import (
"fmt"
"strings"
)
type IProduct interface {
setStock(stock int)
getStock() int
setName(name string)
getName() string
}
type Computer struct {
name string
stock int
}
func (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
}
type Laptop struct {
Computer
}
func newLaptop() IProduct {
return &Laptop{
Computer: Computer{
name: "Laptop computer",
stock: 25,
},
}
}
type Desktop struct {
Computer
}
func newDesktop() IProduct {
return &Desktop{
Computer: Computer{
name: "Desktop computer",
stock: 35,
},
}
}
func GetComputerFactory(computerType string) (IProduct, error) {
if strings.ToLower(computerType) == "laptop" {
return newLaptop(), nil
}
if strings.ToLower(computerType) == "desktop" {
return newDesktop(), nil
}
return nil, fmt.Errorf("Invalid computer type")
}
func printNameAndStock(p IProduct) {
fmt.Printf("Product name: %s, with stock %d\n", p.getName(), p.getStock())
}
func main() {
laptop, _ := GetComputerFactory("laptop")
desktop, _ := GetComputerFactory("desktop")
printNameAndStock(laptop)
printNameAndStock(desktop)
}
package main
import "errors"
// Patron Creacional
// Interface que define el comportamiento de un producto
type IProduct interface {
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 IProduct
func (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 herencia
type 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 pattern
func GetComputerFactory(computerType string) (IProduct, error) {
switch computerType {
case "Laptop":
return NewLaptop(), nil
case "Desktop":
return NewDesktop(), nil
default:
return nil, errors.New("invalid computer type")
}
}
// Trying polymorphism
func PrintNameAndStock(product IProduct) {
println("Name:", product.getName(), "Stock:", product.getStocked())
}
func main() {
laptop, _ := GetComputerFactory("Laptop")
desktop, _ := GetComputerFactory("Desktop")
PrintNameAndStock(laptop)
PrintNameAndStock(desktop)
}
Aquí dejo mi código con comentarios
package main
import "fmt"
// interfaz que cumple la estructura Computer
type IProduct interface {
setStock(stock int)
getStock() int
setName(name string)
getName() string
}
//Las estructuras son literalmente clases
type Computer struct {
name string
stock int
}
//metodos de Computer para satisfacer la interfaz
func (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 Computer
type Laptop struct {
Computer //composition over inheritance
}
// Constructor para crear una nueva laptop
func newLaptop() IProduct {
return &Laptop{
Computer: Computer{
name: "Laptop Computer",
stock: 25,
},
}
}
// Estructura hija de Computer, por lo que obtiene todos sus
// métodos
type Desktop struct {
Computer
}
// Constructor para crear una instancia de la estructura Desktop
func newDesktop() IProduct {
return &Desktop{
Computer{
name: "Desktop Computer",
stock: 35,
},
}
}
// Creamos función para determinar cuál estructura se debe
// instanciar
func GetComputerFactory(computerType string) (IProduct, error) {
if computerType == "laptop" {
return newLaptop(), nil
} else if computerType == "desktop" {
return newDesktop(), nil
}
return nil, fmt.Errorf("Invalid computer type")
}
// imprimimos los productos deseados utilizando la interfaz
func 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 Desktop
printNameAndStoc(laptop)
printNameAndStoc(desktop)
}
Excelente explicación! 😄
Want to see more contributions, questions and answers from the community?