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
}
- 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
}
- 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,
},
}
}
- 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")
}
}
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)
}
Output
Product name: Laptop Computer, with stock 10
Product name: Desktop Computer, with stock 20
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)
}
I hope I have helped you 😊😊
Top comments (3)
I think the newDesktop should not return a laptop :-)
You're rigth, I will correct it
Thanks
This makes a great example now :-)