Design Patterns in Go: Factory method, automating the factory

Introduction

In this article I discussed the implementation of the Abstract Factory pattern. The Factory Method is simply an extension of that pattern. Creating an object can sometimes be complex. A factory-method can abstract this complexity away, and let subclasses or in our case interface implementations decide which objects to create.

It looks like this:

The ConcreteCreator uses an Abstract Factory to build the object. The AbstractCreator just creates an object which implements the Product interface.

It will all become clearer in the code. The code can be found here.

Implementation in Go

If you have cloned the source code, change the imports to look like this:

import (
	"errors"
	"fmt"
)

Now just below that, add this:

type VehicleBrand int

const (
	BrandA VehicleBrand = iota
	BrandB
)

Abstract vehicles

Each of our cars implements the AbstractCar interface, which has only one method: Description() which returns a string description of the vehicle. It looks like this:

type AbstractCar interface {
	Description() string
}

We do something similar for the AbstractBike interface:

type AbstractBike interface {
	Description() string
}

Concrete cars and brand enums

Now it is time to define our brands:

type VehicleBrand int

const (
	BrandA VehicleBrand = iota
	BrandB
)

This is a way to emulate enums in Go. The ‘iota‘ keyword is used to denote successive integer constants. Note that we also define a special type for our VehicleBrand.

Now we can define our first car type:

type BrandACar struct {
	color string
	make  string
}

func NewBrandACar(color string) *BrandACar {
	return &BrandACar{color: color, make: "BrandA"}
}

func (c *BrandACar) Description() string {
	return fmt.Sprintf("Brand A Car - Color: %s, Make: %s", c.color, c.make)
}

Some notes:

  • In our simplified example, a car has a color and a make i.e. the brand.
  • We define a utility function to construct a car of Brand A with the specified color.
  • Also, we implement the AbstractCar interface. The Description() method returns a desciption of the car.

The BrandABike follows the same pattern. The only difference is, that we do not specify the colors but the number of wheels:

type BrandABike struct {
	numberOfWheels int
	make           string
}

func NewBrandABike(numberOfWheels int) *BrandABike {
	return &BrandABike{numberOfWheels: numberOfWheels, make: "BrandA"}
}

func (b *BrandABike) Description() string {
	return fmt.Sprintf("Brand A Bike - Wheels: %d, Make: %s", b.numberOfWheels, b.make)
}

For brand B we do the same:

type BrandBCar struct {
	color string
	make  string
}

func NewBrandBCar(color string) *BrandBCar {
	return &BrandBCar{color: color, make: "BrandB"}
}

func (c *BrandBCar) Description() string {
	return fmt.Sprintf("Brand B Car - Color: %s, Make: %s", c.color, c.make)
}

type BrandBBike struct {
	numberOfWheels int
	make           string
}

func NewBrandBBike(numberOfWheels int) *BrandBBike {
	return &BrandBBike{numberOfWheels: numberOfWheels, make: "BrandB"}
}

func (b *BrandBBike) Description() string {
	return fmt.Sprintf("Brand B Bike - Wheels: %d, Make: %s", b.numberOfWheels, b.make)
}

Creating the factory

Now create an VehicleCreator interface like this:

type VehicleCreator interface {
	createCar(brand VehicleBrand, color string) (AbstractCar, error)
	createBike(brand VehicleBrand, numberOfWheels int) (AbstractBike, error)
}

Our VehicleCreatorImpl struct can only construct cars and bikes. Here is its definition:

type VehicleCreatorImpl struct{}

func (vc *VehicleCreatorImpl) createCar(brand VehicleBrand, color string) (AbstractCar, error) {
	switch brand {
	case BrandA:
		return NewBrandACar(color), nil
	case BrandB:
		return NewBrandBCar(color), nil
	default:
		return nil, errors.New("unknown vehicle brand for car")
	}
}

func (vc *VehicleCreatorImpl) createBike(brand VehicleBrand, numberOfWheels int) (AbstractBike, error) {
	switch brand {
	case BrandA:
		return NewBrandABike(numberOfWheels), nil
	case BrandB:
		return NewBrandBBike(numberOfWheels), nil
	default:
		return nil, errors.New("unknown vehicle brand for bike")
	}
}

A few things to note:

  1. Both methods have a VehicleBrand type parameter, to denote that we are talking about the brand. I did not want to use magic strings because of the possible typos. In case a brand is passed on that is not available, an error is returned.
  2. The returntype is AbstractCar,error or AbstractBike,error. This is in case that for some reason an object could not be created.

Testing it

The testing code looks a bit different now:

func main() {
	var creator VehicleCreator
	creator = &VehicleCreatorImpl{}

	carA, err := creator.createCar(BrandA, "Red")
	if err != nil {
		fmt.Printf("Error creating car: %v\n", err)
		return
	}

	bikeB, err := creator.createBike(BrandB, 2)
	if err != nil {
		fmt.Printf("Error creating bike: %v\n", err)
		return
	}

	fmt.Println(carA.Description())
	fmt.Println(bikeB.Description())
}

A breakdown of this code:

  1. We declare creator to implement the VehicleCreator interface
  2. Next we create the concrete class, and we create a car and a bike and print out their descriptions. If any errors occur they are handled appropiately.

Some possible optimizations of this pattern could be that we cache instances of the different factories, instead of creating them on the fly.

Conclusion

The Abstract Factory pattern provides a clean and scalable way to manage the creation of related objects without tightly coupling your code to specific implementations. In this Go example, we saw how abstract interfaces (AbstractCar, AbstractBike) and concrete types (BrandACar, BrandBBike, etc.) work together through a factory (VehicleCreatorImpl) to produce objects in a flexible, extensible way.

This approach makes it easy to introduce new brands or vehicle types without altering existing code — you simply add new concrete classes and extend the factory. It’s a great demonstration of how the Abstract Factory pattern helps maintain clean separation of concerns while keeping your codebase organized and future-proof.

The Code Nomad
The Code Nomad
Articles: 167

Leave a Reply

Your email address will not be published. Required fields are marked *