Abstract factory pattern generates object factories, which in turn will generate item objects. Each factory is responsible for generating a certain group of item objects.
NOTES
Abstract factory pattern works as a factory of the Factory pattern, so before learning the details of the Abstract factory pattern make sure to have a clear idea about the Factory pattern.
NOTES
In this article, we discuss the implementation of the Abstract Factory Pattern in Go.
See the Abstract Factory in other languages in the “Other Code Implementations” section. Or, use the link below to check the details of the Abstract Factory Pattern-
Implementation
Use the following steps for the implementation-
- Define an item interface.
- Define items (structs) and implement the interface for the items.
- Define interface for the factories. Declare a method that will generate and return an item object. In the following example, we have named it “GetItem”.
- Define factory structs and implement the factory interface.
- Define a producer struct, and include a method that generates a factory and returns that. In the following example, the function name is “GetFactory”.
- For using the pattern, create an object of the factory, using the producer. Then use the factory object to produce item object.
Here is a simple Abstract factory implementation in TypeScript. This is not any specific example, just demo/sample code.
// Abstract Factory Pattern
package main
import (
"errors"
"fmt"
)
// --- Items definition start ---
// Item interface
type Item interface {
Operation1()
}
// First item of type1
type Type1Item1 struct {}
func NewType1Item1() (type1Item1 *Type1Item1) {
type1Item1 = &Type1Item1{}
return
}
func (type1Item1 *Type1Item1) Operation1() {
fmt.Println("Executing: Type 1 - Item 1 - Operation 1")
}
// Second item of type1
type Type1Item2 struct {}
func NewType1Item2() (type1Item1 *Type1Item1) {
type1Item1 = &Type1Item1{}
return
}
func (type1Item1 *Type1Item1) Operation2() {
fmt.Println("Executing: Type 1 - Item 2 - Operation 1")
}
// First item of type2
type Type2Item1 struct {
}
func NewType2Item1() (type2Item1 *Type2Item1) {
type2Item1 = &Type2Item1{}
return
}
func (type2Item1 *Type2Item1) Operation1() {
fmt.Println("Executing: Type 2 - Item 1 - Operation 1")
}
// Second item of type2
type Type2Item2 struct {
}
func NewType2Item2() (type2Item1 *Type2Item1) {
type2Item1 = &Type2Item1{}
return
}
func (type2Item1 *Type2Item1) Operation2() {
fmt.Println("Executing: Type 2 - Item 2 - Operation 1")
}
// --- Items defnition end ---
// --- Factory defnitions start ---
// Factory interface
type Factory interface {
GetItem(identifier string) (Item, error)
}
// type1 factory
type Type1Factory struct {}
func NewType1Factory() (type1Factory *Type1Factory) {
type1Factory = &Type1Factory{}
return
}
func (type1Factory *Type1Factory) GetItem(itemIdentifier string) (Item, error) {
switch itemIdentifier {
case "item1": return NewType1Item1(), nil
case "item2": return NewType1Item2(), nil
default: return nil, errors.New("itemIdentifier not recognized for type1")
}
}
// type2 factory
type Type2Factory struct {}
func NewType2Factory() (type2Factory *Type2Factory) {
type2Factory = &Type2Factory{}
return
}
func (type2Factory *Type2Factory) GetItem(itemIdentifier string) (Item, error) {
switch itemIdentifier {
case "item1": return NewType2Item1(), nil
case "item2": return NewType2Item2(), nil
default: return nil, errors.New("itemIdentifier not recognized for type2")
}
}
// factory producer
type FactoryProducer struct {
}
func NewFactoryProducer() (factoryProdicer *FactoryProducer) {
factoryProdicer = &FactoryProducer{}
return
}
func (factoryProducer *FactoryProducer) GetFactory(typeIdentifier string) (Factory, error) {
switch typeIdentifier {
case "type1": return NewType1Factory(), nil
case "type2": return NewType2Factory(), nil
default: return nil, errors.New("type identifier not recognized")
}
}
// --- Factory definitions end ---
// --- Demo start ---
func main() {
factoryProducer := NewFactoryProducer()
type1Factory, err := factoryProducer.GetFactory("type1")
if err == nil {
type1Item1, err := type1Factory.GetItem("item1")
if err == nil {
type1Item1.Operation1()
}
}
type2Factory, err := factoryProducer.GetFactory("type2")
if err == nil {
type2Item1, err := type2Factory.GetItem("item1")
if err == nil {
type2Item1.Operation1()
}
}
}
// --- Demo end ---
Above code will generate the following output:
Executing: Type 1 - Item 1 - Operation 1
Executing: Type 2 - Item 1 - Operation 1
Examples
Let’s look at a few examples of Abstract factory pattern implementation in Golang.
Example #1: Transport
In this example, we have 2 groups of transport – 2-wheel and 4-wheel. So, we will create 2 factories and use a producer to generate factory objects.
Transport Interface [Item Interface]
- Create file “transport.go”.
- Declare methods – “Start”, “Stop”, and “Repair”.
// transport.go
package main
type Transport interface {
Start()
Stop()
Repair()
}
Bicycle Struct [Item]
- Create a file named “bicycle.go”.
- Create struct “Bicycle”.
- Create method “NewBicycle” which returns a new “Bicycle” object.
- Implement the “Transport” interface, define “Start”, “Stop”, and “Repair” methods for the struct.
// bicycle.go
package main
import "fmt"
type Bicycle struct {
}
func NewBicycle() (bicycle *Bicycle) {
bicycle = &Bicycle{}
return
}
func (bicycle *Bicycle) Start() {
fmt.Println("Bicycle Started")
}
func (bicycle *Bicycle) Stop() {
fmt.Println("Bicycle Stopped")
}
func (bicycle *Bicycle) Repair() {
fmt.Println("Bicycle Repair")
}
Motorcycle Struct [Item]
- Create a file named “motorcycle.go”.
- Create struct “Motorcycle”, and implement “Transport” interface for the struct.
- Create method “NewMotorcycle” to return a new “Motorcycle” object.
// motorcycle.go
package main
import "fmt"
type Motorcycle struct {
}
func NewMotorcycle() (motorcycle *Motorcycle) {
motorcycle = &Motorcycle{}
return
}
func (motorcycle *Motorcycle) Start() {
fmt.Println("Motorcycle Started")
}
func (motorcycle *Motorcycle) Stop() {
fmt.Println("Motorcycle Stopped")
}
func (motorcycle *Motorcycle) Repair() {
fmt.Println("Motorcycle Repair")
}
Car Struct [Item]
- Create a file named “car.go”.
- Define “Car” item struct and implement “Transport” interface.
// car.go
package main
import "fmt"
type Car struct {
}
func NewCar() (car *Car) {
car = &Car{}
return
}
func (car *Car) Start() {
fmt.Println("Car Started")
}
func (car *Car) Stop() {
fmt.Println("Car Stopped")
}
func (car *Car) Repair() {
fmt.Println("Car Repair")
}
Truck Struct [Item]
- Create a file named “truck.go”.
- Define “Truck” struct and implement “Transport” interface for “Truck”.
// truck.go
package main
import "fmt"
type Truck struct {
}
func NewTruck() (truck *Truck) {
truck = &Truck{}
return
}
func (truck *Truck) Start() {
fmt.Println("Truck Started")
}
func (truck *Truck) Stop() {
fmt.Println("Truck Stopped")
}
func (truck *Truck) Repair() {
fmt.Println("Truck Repair")
}
Transport Factory Interface
- Create a file named “abstract_transport-factory.go”.
- Define an interface named “TransportFactory”. This interface will be used by all factory.
- Declare a method to generate and return an item object. Here we have named it “GetTransport”.
// transport_factory.go
package main
type TransportFactory interface {
GetTransport(identifier string) (Transport, error)
}
Two-Wheel Transport Factory
- Create a file named “two_wheel_transport_factory.go”.
- Create a struct named “TwoWheelTransportFactory”. This factory is responsible for generating items of 2 wheel vehicles.
- Create method named “NewTwoWheelTransportFactory”.
- Implement the factory interface “TransportFactory” by defining the method “GetTransport” for the struct.
// two_wheel_transport_factory.go
package main
import (
"errors"
"strings"
)
type TwoWheelTransportFactory struct {
}
func NewTwoWheelTransportFactory() (twoWheelTransportFactory *TwoWheelTransportFactory) {
twoWheelTransportFactory = &TwoWheelTransportFactory{}
return
}
func (twoWheelTransportFactory *TwoWheelTransportFactory) GetTransport(identifier string) (Transport, error) {
identifier = strings.ToLower(identifier)
switch identifier {
case "bicycle": return NewBicycle(), nil
case "motorcycle": return NewMotorcycle(), nil
default: return nil, errors.New("identifier does not match with any defined two wheel transport")
}
}
Four-Wheel Transport Factory
- Create a file named “four_wheel_transport_factory.go”.
- Create struct “FourWheelTransportFactory”.
- Define method “GetTrasnport” for the factory, as part of “TransportFactory” interface implementation.
// four_wheel_transport_factory.go
package main
import (
"errors"
"strings"
)
type FourWheelTransportFactory struct {
}
func NewFourWheelTransportFactory() (fourWheelTransportFactory *FourWheelTransportFactory) {
fourWheelTransportFactory = &FourWheelTransportFactory{}
return
}
func (fourWheelTransportFactory *FourWheelTransportFactory) GetTransport(identifier string) (Transport, error) {
identifier = strings.ToLower(identifier)
switch identifier {
case "car":
return NewCar(), nil
case "truck":
return NewTruck(), nil
default:
return nil, errors.New("identifier does not match with any defined four wheel transport")
}
}
Factory Producer
- Create a file named “transport_factory_producer.go”.
- Create struct “TransportFactoryProducer”. This is the producer of factory.
- Define method “GetFactory” which generates factory object and return that.
// transport_factory_producer.go
package main
import "errors"
type TransportFactoryProducer struct {
}
func NewTransportFactoryProducer() (transportFactoryProducer *TransportFactoryProducer) {
transportFactoryProducer = &TransportFactoryProducer{}
return
}
func (transportFactoryProducer *TransportFactoryProducer) GetFactory(numberOfWheels int) (TransportFactory, error) {
switch numberOfWheels {
case 2:
return NewTwoWheelTransportFactory(), nil
case 4:
return NewFourWheelTransportFactory(), nil
default:
return nil, errors.New("number of wheels does not match with any predefined tyep")
}
}
Demo
- Create a file named “main.go”.
- Create a new object of producer and save it in “transportFactoryProducer”.
- Use method “GetFactory” of the producer to get a factory object.
- Then use the factory object to get transport item object.
// main.go
package main
func main() {
transportFactoryProducer := NewTransportFactoryProducer()
twoWheelFactory, err := transportFactoryProducer.GetFactory(2)
if err == nil {
bicycle, err := twoWheelFactory.GetTransport("bicycle")
if err == nil {
bicycle.Start()
}
}
fourWheelFactory, err := transportFactoryProducer.GetFactory(4)
if err == nil {
truck, err := fourWheelFactory.GetTransport("truck")
if err == nil {
truck.Start()
truck.Stop()
truck.Repair()
}
}
}
Output
Here is the output of the demo code.
Bicycle Started
Truck Started
Truck Stopped
Truck Repair
Source Code
Use the following link to get the source code:
Example | Source Code Link |
---|---|
Example #1: Transport | GitHub |
Other Code Implementations
Use the following links to check Abstract Factory pattern implementation in other programming languages.