DEV Community

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

Posted on • Edited on

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 😊😊

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