Design Pattern: Factory Pattern in Go

Factory pattern abstracts the object creation process and logic from the client. We use the Factory pattern to generate object, when we don’t want to instantiate the object directly.

NOTES

In this article, we discuss the implementation of the Factory Pattern in Go.

See the Factory in other languages in the “Other Code Implementations” section. Or, use the link below to check the details of the Factory Pattern-

Implementation

Following these steps to implement Factory pattern in Go-

  • Create an interface, that will ensure a common interface for all the item objects. Here we have created “Base” interface.
  • Define one struct for each item and implement “Base” interface. We have declared here 2 structs – ExampleStruct1, and ExampleStruct2.
  • Define a struct for the factory. Here we have the “Factory” struct.
  • For the factory, define a method for generating the object. Here we have “GetExampleObject”. It takes a string as an identifier, generates an object based on that, then returns the object. 
  • For using the factory create an object of the factory and then use “GetExampleObject” method to generate the desired object.

Here is a simple example of Factory pattern implementation in Golang-

// Factory Pattern

package main

import (
	"errors"
	"fmt"
	"strings"
)

// ---  Declare Base interface ---

type Base interface {
	Operation1()

	Operation2()
}

// --- Interface definition end ---

// --- Define multiple item structs and implement Base interface ---

// Define first item struct
type ExampleStruct1 struct {
}

func NewExampleStruct1() (exampleStruct1 *ExampleStruct1) {
	exampleStruct1 = &ExampleStruct1{}
	// Set properties if any

	return
}

func (exampleStruct1 *ExampleStruct1) Operation1() {
	fmt.Println("Executing: ExampleClass1 Operation1")
}

func (exampleStruct1 *ExampleStruct1) Operation2() {
	fmt.Println("Executing: ExampleClass1 Operation2")
}

// Define another item struct
type ExampleStruct2 struct {
}

func NewExampleStruct2() (exampleStruct2 *ExampleStruct2) {
	exampleStruct2 = &ExampleStruct2{}
	// Set properties if any

	return
}

func (exampleStruct2 *ExampleStruct2) Operation1() {
	fmt.Println("Executing: ExampleClass2 Operation1")
}

func (exampleStruct2 *ExampleStruct2) Operation2() {
	fmt.Println("Executing: ExampleClass2 Operation2")
}

// --- Item struct definition end here ---

// --- Define factory ---

type Factory struct {
}

func NewFactory() (factory *Factory) {
	factory = &Factory{}
	return
}

func (factory *Factory) GetExampleObj(identifier string) (Base, error) {
	identifier = strings.ToLower(identifier)

	// Generate object based on the identifier passed
	switch identifier {
		case "example_struct_1": return NewExampleStruct1(), nil
		case "example_struct_2": return NewExampleStruct2(), nil
		default: return nil, errors.New("unknown identifier")
	}
}

// --- Factory definition end ---

// --- Demo ---

func main() {
	// Create a factory object
	exampleFactory := NewFactory()

	// Use the factory object to generate the item object
	exampleObj1, err := exampleFactory.GetExampleObj("example_struct_1")

	if err == nil {
		exampleObj1.Operation1()
		exampleObj1.Operation2()
	}

	// Use factory to generate another item object
	exampleObj2, err := exampleFactory.GetExampleObj("example_struct_2")

	if err == nil {
		exampleObj2.Operation1()
		exampleObj2.Operation2()
	}
}

// --- Demo end ---

Output of the above code will be as below.

Executing: ExampleClass1 Operation1
Executing: ExampleClass1 Operation2

Executing: ExampleClass2 Operation1
Executing: ExampleClass2 Operation2

Examples

Let’s take a look at a few examples of Factory pattern implementation in Golang.

Example #1: Transport

Let’s take the example of a transport system. Here we have 3 transport items- “Bus”, “Car”, and “Plane”. Factory pattern is used to generate the item objects.

Let’s take a look at the implementation step-by-step.

Transport Interface

  • Create a file named “transport.go”.
  • Declare “Transport” interface. Here we have declared 3 methods for the interface – “Start”, “Stop”, and “Repair”.
// transport.go

package main

type Transport interface {
	Start()
	Stop()
        Repair()
}

Bike Struct [Item]

  • Create a file for the “Bike” struct and name it “bike.go”.
  • Create “NewBike” method, which can create new “Bike” object and return it.
  • Implement the “Transport” interface for Bike. Define “Start”, “Stop” and “Repair” method as part of the interface implementation.
// bike.go

package main

import "fmt"

type Bike struct {
}

func NewBike() (bike *Bike) {
	bike = &Bike{}
	return
}

func (bike *Bike) Repair() {
	fmt.Println("Bike Repair")
}

func (bike *Bike) Start() {
	fmt.Println("Bike started")
}

func (bike *Bike) Stop() {
	fmt.Println("Bike Stopped")
}

Car Struct [Item]

  • Create a file “car.go”, and define the item struct “Car” in the file.
  • Implement “Transport” interface for “Car”.
// car.go

package main

import "fmt"

type Car struct {
}

func NewCar() (car *Car) {
	car = &Car{}
	return
}

func (car *Car) Repair() {
	fmt.Println("Car Repair")
}

func (car *Car) Start() {
	fmt.Println("Car started")
}

func (car *Car) Stop() {
	fmt.Println("Car Stopped")
}

Plane Struct [Item]

  • Create file named “plange.go”.
  • Define “Plane” struct and implement “Transport” interface for the struct.
// plane.go

package main

import "fmt"

type Plane struct {
}

func NewPlane() (plane *Plane) {
	plane = &Plane{}
	return
}

func (plane *Plane) Repair() {
	fmt.Println("Plane Repair")
}

func (plane *Plane) Start() {
	fmt.Println("Plane started")
}

func (plane *Plane) Stop() {
	fmt.Println("Plane Stopped")
}

Transport Factory

  • Create file named “transport_factory.go”.
  • Define “TransportFactory” struct and create a method named “NewTransportFactory” for generating a new object of “TransportFactory”.
  • Create a method named “GetTransport” for the struct. This method will take an identifier (string in this case), then will generate and return an object based on the identifier.
// transport_factory.go

package main

import (
	"errors"
	"strings"
)

type TransportFactory struct {
}

func NewTransportFactory() (transportFactory *TransportFactory) {
	transportFactory = &TransportFactory{}
	return
}

func (transportFactory *TransportFactory) GetTransport(identifier string) (Transport, error) {
	if strings.ToLower(identifier) == "bike" {
		return NewBike(), nil
	}

	if strings.ToLower(identifier) == "car" {
		return NewCar(), nil
	}

	if strings.ToLower(identifier) == "plane" {
		return NewPlane(), nil
	}

	return nil, errors.New("Unknown Transport Identifier")
}

Demo

  • Create a file “main.go”. Declare the “main” function in the file.
  • Create an object of “TransportFactory” using the “NewTransportFactory” method.
  • Then call the “GetTransport” method of “TransportFactory” to generate object, and also pass the designed identifier (bike, car, plane, etc.) to the function. This will generate an object and return it.
  • Call methods (Start, Stop, Repair) of the transport object for using the functionality.
// main.go

package main

func main() {
	transportFactory := NewTransportFactory()
	transport1, err := transportFactory.GetTransport("bike")

	if err == nil {
		transport1.Start()
	}

	transport2, err := transportFactory.GetTransport("car")

	if err == nil {
		transport2.Start()
		transport2.Stop()
	}
	
	transport3, err := transportFactory.GetTransport("plane")
	
	if err == nil {
		transport3.Start()
	}
}

Output

We will get the following output if the above demo code is executed.

Bike started

Car started
Car Stopped

Plane started

Example #2: Transport [Factory Method]

This example demonstrates the Factory method implementation for transport.

In this implementation, we have 2 types of transport- road transport and air transport. Multiple item classes belong to those types of transport.

Transport Interface

  • Create file named “transport.go”.
  • Declare “Transport” interface, include 3 methods “Repair”, “Start”, “Stop” in the declaration.
// transport.go

package main

type Transport interface {
	Repair()
	Start()
	Stop()
}

Bus Struct [Road Transport Item]

  • Create file named “bus.go” for the “Bus” item.
  • Define “Bus” struct and implement “Transport” interface for that.
  • Also define method “NewBus()” for generating a new “Bus” object.
// bus.go

package main

import "fmt"

type Bus struct {
}

func NewBus() (bus *Bus) {
	bus = &Bus{}
	return
}

func (bus *Bus) Repair() {
	fmt.Println("Bus Repair")
}

func (bus *Bus) Start() {
	fmt.Println("Bus started")
}

func (bus *Bus) Stop() {
	fmt.Println("Bus Stopped")
}

Bike Struct [Road Transport Item]

  • Create file named “bike.go” for the “Bike” item.
  • Define “Bike” struct and implement “Transport” interface.
  • Also, define the method “NewBike” for generating a new “Bike” object.
// bike.go

package main

import "fmt"

type Bike struct {
}

func NewBike() (bike *Bike) {
	bike = &Bike{}
	return
}

func (bike *Bike) Repair() {
	fmt.Println("Bike Repair")
}

func (bike *Bike) Start() {
	fmt.Println("Bike started")
}

func (bike *Bike) Stop() {
	fmt.Println("Bike Stopped")
}

Car Struct [Road Transport Item]

  • Create a file named “car.go”.
  • Define “Car” struct.
  • Implement “Transport” interface for the “Car”.
  • Also, define method “NewCar” for generating a new “Car” object.
// car.go

package main

import "fmt"

type Car struct {
}

func NewCar() (car *Car) {
	car = &Car{}
	return
}

func (car *Car) Repair() {
	fmt.Println("Car Repair")
}

func (car *Car) Start() {
	fmt.Println("Car started")
}

func (car *Car) Stop() {
	fmt.Println("Car Stopped")
}

Plane Struct [Air Transport Item]

  • Create file “plane.go”.
  • Define “Plane” struct and implement “Transport” interface for this.
// plane.go

package main

import "fmt"

type Plane struct {
}

func NewPlane() (plane *Plane) {
	plane = &Plane{}
	return
}

func (plane *Plane) Repair() {
	fmt.Println("Plane Repair")
}

func (plane *Plane) Start() {
	fmt.Println("Plane started")
}

func (plane *Plane) Stop() {
	fmt.Println("Plane Stopped")
}

Helicopter Struct [Air Transport Item]

  • Create a file named “helicopter.go”.
  • Define “Helicopter” struct.
  • Implement “Transport” interface for “Helicopter” struct.
// helicopter.go

package main

import "fmt"

type Helicopter struct {
}

func NewHelicopter() (helicopter *Helicopter) {
	helicopter = &Helicopter{}
	return
}

func (helicopter *Helicopter) Repair() {
	fmt.Println("Helicopter Repair")
}

func (helicopter *Helicopter) Start() {
	fmt.Println("Helicopter started")
}

func (helicopter *Helicopter) Stop() {
	fmt.Println("Helicopter Stopped")
}

Transport Factory

Let’s define the factory implementation.

We have the main “TransportFactory” and then 2 child factories (“RoadTransportFactory” and “AirTransportFactory”).

TransportFactory” acts as an abstract class here. As we can not directly implement an abstract class in Go, so the “TransportFactory” and other factory implementation becomes a little bit tricky.

  • Create a file named “transport_factory.go”.
  • Declare “TransportFactoryInterface” and declare 3 methods there.
  • Define “TransportFactory” struct and embed the “TransportFactoryInterface” in that.
  • Define method for generating a new object of “TransportFactory” named “NewTransportFactory”.
  • Define “OperateTransport” and “RepairTransport” methods as part of the interface “TransportFactoryInterface”.
  • Do not define the “GetTransport” method. This method is used to generate new objects of transport and will be defined separately in the child transport factories.
// transport_factory.go

package main

type TransportFactoryInterface interface {
	GetTransport(name string) (Transport, error)
	OperateTransport(name string)
	RepairTransport(name string)
}

type TransportFactory struct {
	TransportFactoryInterface
}

func NewTransportFactory() (transportFactory *TransportFactory) {
	transportFactory = &TransportFactory{}
	return
}

func (transportFactory *TransportFactory) OperateTransport(name string) {
	transport, err := transportFactory.GetTransport(name)

	if err == nil {
		transport.Start()
		transport.Stop()
	}
}

func (transportFactory *TransportFactory) RepairTransport(name string) {
	transport, err := transportFactory.GetTransport(name)

	if err == nil {
		transport.Repair()
	}
}

Road Transport Factory

  • Create a file “road_transport_factory.go”.
  • Define struct for the factory for the road transports. Here we named it “RoadTransportFactory”. 
  • Define method “NewRoadTransportFactory” that can generate and return a new “RoadTransportFactory” object.
  • Define the “GetTransport” method. Return any of the “Car”, “Bike” or “Bus” object based on the “name”(identifier) param.
// road_transport_factory.go

package main

import (
	"errors"
	"strings"
)

type RoadTransportFactory struct {
	TransportFactory
}

func NewRoadTransportFactory() (roadTransportFactory *RoadTransportFactory) {
	roadTransportFactory = &RoadTransportFactory{TransportFactory{}}
	roadTransportFactory.TransportFactory.TransportFactoryInterface = roadTransportFactory
	return
}

func (roadTransportFactory *RoadTransportFactory) GetTransport(name string) (Transport, error) {
	if strings.ToLower(name) == "car" {
		return NewCar(), nil
	}

	if strings.ToLower(name) == "bike" {
		return NewBike(), nil
	}

	if strings.ToLower(name) == "bus" {
		return NewBus(), nil
	}

	return nil, errors.New("unknown identifier for RoadTransportFactory")
}

Air Transport Factory

  • Create a file “air_transport_factory.go”.
  • Define “AirTransportFactory” struct, and embed the “TransportFactory” into it, which in turn embeds the “TransportFactoryInterface”.
  • As a part of the interface implementation define the “GetTransport” method. Return the “Plane” or “Helicopter” object based on the param passed.
// air_transport_factory.go

package main

import (
	"errors"
	"strings"
)

type AirTransportFactory struct {
	TransportFactory
}

func NewAirTransportFactory() (airTransportFactory *AirTransportFactory) {
	airTransportFactory = &AirTransportFactory{}
	airTransportFactory.TransportFactory.TransportFactoryInterface = airTransportFactory
	return
}

func (airTransportFactory *AirTransportFactory) GetTransport(name string) (Transport, error) {
	if strings.ToLower(name) == "plane" {
		return NewPlane(), nil
	}

	if strings.ToLower(name) == "helicopter" {
		return NewHelicopter(), nil
	}

	return nil, errors.New("unknown identifier for AirTransportFactory")
}

Demo

  • Create a file named “main.go” and define function “main”.
  • Create object of “RoadTransportFactory” and “AirTransportFactory”.
  • Call the “OperateTransport” and/or “RepairTransport” methods of the factory while passing item identifier(“bus”, “helicopter”, “bike” etc.) to the method.
// main.go

package main

func main() {
	roadTransportFactory := NewRoadTransportFactory()
	airTransportFactory := NewAirTransportFactory()

	roadTransportFactory.OperateTransport("bus")
	airTransportFactory.OperateTransport("helicopter")
	roadTransportFactory.RepairTransport("bike")
}

Output

The above demo code will generate output as below.

Bus started
Bus Stopped

Helicopter started
Helicopter Stopped

Bike Repair

Source Code

Use the following link to get the source code:

Other Code Implementations

Use the following links to check Factory pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.