DEV Community

Cristian Francisco Meoño Canel
Cristian Francisco Meoño Canel

Posted on • Edited on

4 3

Go Design Patterns : Factory

Factory

It is a creational pattern that allows us to create a "factory" of objects from a base class and to implement polymorphic behaviors that allow us to modify the behavior of the inherited classes.

It is usually used to reuse the creation logic that shares a series of objects.

Go is not strictly an object-oriented language, but we can implement OOP logic through interfaces and the concept of composition over inheritance.

Example
We have a series of products (objects) that behave in a similar way. We can either make a separate class for each one or put all this logic in a base class to create inherited classes for each product.

  • First, we need to define an interface that describes the behaviors of our products "made" by our "factory".
type IProduct interface {
    setStock(stock int)
    getStock() int
    setName(name string)
    getName() string
}
Enter fullscreen mode Exit fullscreen mode
  • Define how our product look, then implement the IProduct methods in order to comply with the interface. This is similar to define an abstract class.
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
}
Enter fullscreen mode Exit fullscreen mode
  • Create our product classes that are inherited from the Computer class. Here the concept of composition over inheritance is applied.
type Laptop struct {
    Computer
}

func newLaptop() IProduct {
    return &Laptop{
        Computer: Computer{
            name:  "Laptop Computer",
            stock: 0,
        },
    }
}

type Desktop struct {
    Computer
}

func newDesktop() IProduct {
    return &Desktop{
        Computer: Computer{
            name:  "Desktop Computer",
            stock: 0,
        },
    }
}

Enter fullscreen mode Exit fullscreen mode
  • Finally, we need to "create" our factory method, this method will return a instance of the object specified.
func GetComputerFactory(computerType string) (IProduct, error) {
    if computerType == "laptop" {
        return newLaptop(), nil
    } else if computerType == "desktop" {
        return newDesktop(), nil
    } else {
        return nil, fmt.Errorf("invalid computer type")
    }
}
Enter fullscreen mode Exit fullscreen mode

Running our code

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")

    laptop.setStock(10)
    PrintNameAndStock(laptop)
    desktop.setStock(20)
    PrintNameAndStock(desktop)

}
Enter fullscreen mode Exit fullscreen mode

Output

Product name: Laptop Computer, with stock 10
Product name: Desktop Computer, with stock 20
Enter fullscreen mode Exit fullscreen mode

Full Example

package main

import "fmt"

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) getStock() int {
    return c.stock
}

func (c *Computer) setName(name string) {
    c.name = name
}

func (c *Computer) getName() string {
    return c.name
}

type Laptop struct {
    Computer
}

func newLaptop() IProduct {
    return &Laptop{
        Computer: Computer{
            name:  "Laptop Computer",
            stock: 0,
        },
    }
}

type Desktop struct {
    Computer
}

func newDesktop() IProduct {
    return &Desktop{
        Computer: Computer{
            name:  "Desktop Computer",
            stock: 0,
        },
    }
}

func GetComputerFactory(computerType string) (IProduct, error) {
    if computerType == "laptop" {
        return newLaptop(), nil
    } else if computerType == "desktop" {
        return newDesktop(), nil
    } else {
        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")

    laptop.setStock(10)
    PrintNameAndStock(laptop)
    desktop.setStock(20)
    PrintNameAndStock(desktop)
}

Enter fullscreen mode Exit fullscreen mode

I hope I have helped you 😊😊

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (3)

Collapse
 
marcello_h profile image
Marcelloh

I think the newDesktop should not return a laptop :-)

Collapse
 
cristianfrancisco85 profile image
Cristian Francisco Meoño Canel

You're rigth, I will correct it
Thanks

Collapse
 
marcello_h profile image
Marcelloh

This makes a great example now :-)

Image of Datadog

How to Diagram Your Cloud Architecture

Cloud architecture diagrams provide critical visibility into the resources in your environment and how they’re connected. In our latest eBook, AWS Solution Architects Jason Mimick and James Wenzel walk through best practices on how to build effective and professional diagrams.

Download the Free eBook

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay