Design Patterns | Factory

Creational pattern - Factory

Use cases:

  • when you don’t know what exact types and dependencies your code should support
  • when you want to extend some properties of objects that you provide
  • when you want to save resources by providing existing objects

Pros:

  • avoid coupling of between creator and concrete type
  • ability to add new types without breaking old code
  • more control on code because there are one place where your objects are creating

Example:

Define Product interface with single method GetCost():

1
2
3
type Product interface {
GetCost() int
}

Define two concrete types of Product:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Phone struct {
cost int
}

func NewPhone(cost int) *Phone {
return &Phone{cost}
}
func (p *Phone) GetCost() int {
return p.cost
}

const CarbonEmissionCost = 1000

type Car struct {
cost int
}

func NewCar(cost int) *Car {
return &Car{cost}
}
func (c *Car) GetCost() int {
return CarbonEmissionCost + c.cost
}

Define factory that will create for us Product types:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type ProductType int

const (
PhoneType ProductType = iota
CarType
)

type ProductFactory struct {
}

func (s *ProductFactory) NewProduct(p ProductType, cost int) Product {
switch p {
case PhoneType:
return NewPhone(cost)
case CarType:
return NewCar(cost)
}
return nil
}

Example of usage:

1
2
3
4
5
6
7
8
9
func main() {
products := []factory.Product{factory.NewCar(2000), factory.NewPhone(800)}
totalCost := 0
for _, v := range products {
totalCost += v.GetCost()
}
fmt.Printf("Total cost: %v$ ", totalCost)
}