Abstract Factory Design Pattern: A Practical Dive with Golang


Introduction

In software engineering, designing with principles such as SOLID is crucial. The SOLID principle emphasizes Interface Segregation, which advocates for using multiple, client-specific interfaces. Building on this idea, the Abstract Factory design pattern becomes relevant as it encapsulates object creation, enabling a system to generate families of related objects without specifying their concrete classes. In this post, we’ll delve into the Abstract Factory pattern through a practical Go example, exploring its implementation and benefits.

Setting the Stage: What is the Abstract Factory Pattern?

The Abstract Factory pattern offers an interface to create families of related or dependent objects without directly specifying their concrete classes. In essence, you create objects that then spawn other objects, based on certain criteria.

Let’s take a quick peek into the initial setup:

type SmallVehicle interface {
	DriveSmallVehicle()
}

type LargeVehicle interface {
	DriveLargeVehicle()
}

Here, we’ve defined two abstract product interfaces based on size: SmallVehicle and LargeVehicle.

The Players: Concrete Products

Moving on, for each abstract product, we have specific concrete implementations:

type JetSki struct{}

func (j JetSki) DriveSmallVehicle() {
	fmt.Println("Driving JetSki!")
}

type Motorcycle struct{}

func (j Motorcycle) DriveSmallVehicle() {
	fmt.Println("Driving Motorcycle!")
}

The JetSki and Motorcycle are the smaller vehicles, implementing the DriveSmallVehicle method from the SmallVehicle interface.

Similarly, for the larger ones:

type Boat struct{}

func (b Boat) DriveLargeVehicle() {
	fmt.Println("Driving Boat!")
}

type Car struct{}

func (b Car) DriveLargeVehicle() {
	fmt.Println("Driving Car!")
}

Both Boat and Car are concrete implementations of the LargeVehicle interface.

Orchestrating Creation: The Abstract Factory

For a design pattern that carries the title “Factory”, this is where the magic happens:

type VehicleFactory interface {
	CreateSmallVehicle() SmallVehicle
	CreateLargeVehicle() LargeVehicle
}

Our VehicleFactory interface provides a blueprint for the creation of both small and large vehicles.

Bringing it to Life: Concrete Factories

Concrete factories, as the name implies, make the actual products:

type LandFactory struct{}

func (l LandFactory) CreateSmallVehicle() SmallVehicle {
	return Motorcycle{}
}

func (l LandFactory) CreateLargeVehicle() LargeVehicle {
	return Car{}
}

The LandFactory produces Motorcycle and Car, both suited for land terrain. Similarly:

type SeaFactory struct{}

func (l SeaFactory) CreateSmallVehicle() SmallVehicle {
	return JetSki{}
}

func (l SeaFactory) CreateLargeVehicle() LargeVehicle {
	return Boat{}
}

For water enthusiasts, the SeaFactory provides the JetSki and the Boat.

Client Interaction and Factory Selection

Finally, how does the end user (or the client code) interact with our system?

// terrainType is passed in via command line for this example, but could also be // config or environment variable
factory, err := GetVehicleFactory(terrainType)
if err != nil {
	fmt.Printf("Error: %v\\\\n", err)
	os.Exit(1)
}

switch passengerType {
case "small":
	factory.CreateSmallVehicle().DriveSmallVehicle()
case "large":
	factory.CreateLargeVehicle().DriveLargeVehicle()
default:
	fmt.Printf("Invalid passenger type provided: %s\\\\n", passengerType)
	os.Exit(1)
}

This segment of the code selects the appropriate factory based on the provided terrain, and then creates the vehicle of the specified size.

Conclusion

The Abstract Factory pattern offers an elegant solution for situations where a system must create and manage families or categories of related objects. By segregating the object creation logic based on distinct criteria (like size and terrain in our example), we achieve a scalable and maintainable design.

As demonstrated with our Golang example, the pattern’s beauty lies in its simplicity and versatility.

Happy coding!

Email: [email protected]
Linkedin: https://www.linkedin.com/in/eric-howard-8a4166127/