Design Patterns | Abstract Factory

Creational pattern - Abstract Factory

Use cases:

  • when you need to work with related classes while decoupling it on concrete classes
  • Consumer of your factory can easily use your factory methods and do not worry about compatibility of objects from different sub types

Example:

Define iShirt and shirt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package abstract_factory

type iShirt interface {
Size() int
SetSize(size int)
Logo() string
SetLogo(logo string)
}

type shirt struct {
logo string
size int
}

func (s *shirt) SetLogo(logo string) {
s.logo = logo
}
func (s *shirt) Logo() string {
return s.logo
}
func (s *shirt) SetSize(size int) {
s.size = size
}
func (s *shirt) Size() int {
return s.size
}

Define iShoe and shoe:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package abstract_factory

type shoe struct {
logo string
size int
}
type iShoe interface {
Size() int
SetSize(size int)
Logo() string
SetLogo(logo string)
}

func (s *shoe) SetLogo(logo string) {
s.logo = logo
}
func (s *shoe) Logo() string {
return s.logo
}
func (s *shoe) SetSize(size int) {
s.size = size
}
func (s *shoe) Size() int {
return s.size
}

Define concretenike:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package abstract_factory

type nike struct{}

type nikeShirt struct {
shirt
}
type nikeShoe struct {
shoe
}

func (n *nike) Shoe() iShoe {
return &nikeShoe{shoe: shoe{logo: "Nike", size: 10}}
}
func (n *nike) Shirt() iShirt {
return &nikeShirt{shirt: shirt{logo: "Nike", size: 20}}
}

Define concreteadidas:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package abstract_factory

type adidasShirt struct {
shirt
}
type adidasShoe struct {
shoe
}

type adidas struct{}

func (n *adidas) Shoe() iShoe {
return &adidasShoe{shoe: shoe{logo: "Adidas", size: 14}}
}
func (n *adidas) Shirt() iShirt {
return &adidasShirt{shirt: shirt{logo: "Adidas", size: 30}}
}

Define iWearFactory:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package abstract_factory

type iWearFactory interface {
Shoe() iShoe
Shirt() iShirt
}

type Brands int

const (
Nike Brands = iota
Adidas
)

func NewWearFactory(brand Brands) iWearFactory {
switch brand {
case Nike:
return &nike{}
case Adidas:
return &adidas{}
}
return nil
}

Simple usage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
adidasFactory := abstract_factory.NewWearFactory(abstract_factory.Adidas)
nikeFactory := abstract_factory.NewWearFactory(abstract_factory.Nike)

shoe1 := nikeFactory.Shoe()
shirt1 := nikeFactory.Shirt()
shoe2 := adidasFactory.Shoe()
shirt2 := adidasFactory.Shirt()
fmt.Printf("%v \n", shoe1)
fmt.Printf("%v \n", shoe2)
fmt.Printf("%v \n", shirt1)
fmt.Printf("%v \n", shirt2)
}