Hello, Habr! I present to your attention the translation of the next article "Design Patterns: Abstract Factory Pattern" by Shubham Zanwar.
An abstract factory is a generative design pattern. It is used when we need to create a family of similar products. Let's take an example of a pizza chain for understanding.
Pizzeria
Let's say you are the head of a business and you open a chain of pizzerias throughout the city. One of your responsibilities is the production of all the staple products (in our case, pizzas and fried garlic bread), which will be represented by brands such as Domino and Roaster (let's call them so - approx. Transl.).
There are many ways to do this. The easiest is to create a pizza factory for each brand and a similar one for fried bread.
If you still have no idea how factories work, you can read here
The problem is that we now trust the user to choose the right type of pizza and toasted bread they want. If they make the mistake of making Domino's Pizza with Roaster's Garlic Bread, your customers will be furious and you could lose your contract with these brands.
, .
( ), . .
, , .
. , :
type iPizza interface {
GetPrice() float64
GetName() string
GetToppings() []string
}
type pizza struct {
name string
price float64
toppings []string
}
func (p *pizza) GetName() string {
return p.name
}
func (p *pizza) GetPrice() float64 {
return p.price
}
func (p *pizza) GetToppings() []string {
return p.toppings
}
type pizzaHutPizza struct {
pizza
}
type dominosPizza struct {
pizza
}
type iGarlicBread interface {
GetPrice() float64
GetName() string
}
type garlicBread struct {
name string
price float64
}
func (g *garlicBread) GetName() string {
return g.name
}
func (g *garlicBread) GetPrice() float64 {
return g.price
}
type pizzaHutGarlicBread struct {
garlicBread
}
type dominosGarlicBread struct {
garlicBread
}
, , . .
,
type iPizzaFactory interface {
createPizza() iPizza
createGarlicBread() iGarlicBread
}
: - -
type PizzaHutFactory struct {}
func (p *PizzaHutFactory) createPizza(): iPizza {
return &pizzaHutPizza{
pizza{
name: "pepperoni",
price: 230.3,
toppings: []string{"olives", "mozzarella", "pork"},
},
}
}
func (p *pizzaHutFactory) createGarlicBread() iGarlicBread {
return &pizzaHutGarlicBread{
garlicBread{
name: "garlic bread",
price: 180.99,
},
}
}
type dominosFactory struct{}
func (d *dominosFactory) createPizza() iPizza {
return &dominosPizza{
pizza{
name: "margherita",
price: 200.5,
toppings: []string{"tomatoes", "basil", "olive oil"},
},
}
}
func (d *dominosFactory) createGarlicBread() iGarlicBread {
return &dominosGarlicBread{
garlicBread{
name: "cheesy bread sticks",
price: 150.00,
},
}
}
, /.
. , . ? .
. , ( ), ( ). "", .
-
func getPizzaFactory(chain string) (iPizzaFactory, error) {
if chain == "P" {
return &pizzaHutFactory{}, nil
}
if chain == "D" {
return &dominosFactory{}, nil
}
return nil, fmt.Errorf("Enter a valid chain type next time")
}
, .
The main thing to remember is that the abstract factory pattern implements a factory factory. In-house factories are used to create the right kind of products.
You can find this code on github
While