Desing Patterns in Golang: Factory Method

Introduction

The Factory Method pattern is a design pattern used to define a runtime interface for creating an object. It’s called a factory because it creates various types of objects without necessarily knowing what kind of object it creates or how to create it.

Purpose

Design Pattern Diagram

The structs and objects participating in this pattern are: product, concreate product, creator and concrete creator. The Creator contains one method to produce one type of product related to its type.

Builder Class Diagram

Implementation

The Factory Method defines an interface for creating objects, but lets subclasses decide which classes to instantiate. In these example, we will adopt the pattern to create document object model of Scalable Vector Graphics.

The SVG format can contains multiple elements. In this example, we will illustrate only some of the shape elements. In the context of Factory Method design pattern, they are our product.

Every shape implements the Shape interface, which expose a Draw function that generates the required XML element:

type Shape interface {
	Draw(io.Writer) error
}

In the following code snippets, we will illustrate two implementations of Shape interface Circle and Ractangle:

type Circle struct {
	Location Point
	Radius   float64
}

func (c *Circle) Draw(w io.Writer) error {
	_, err := fmt.Fprintf(w, `<circle cx="%f" cy="%f" r="%f"/>`, c.Location.X, c.Location.Y, c.Radius)
	return err
}
type Rectangle struct {
	Location Point
	Size     Size
}

func (rect *Rectangle) Draw(w io.Writer) error {
	_, err := fmt.Fprintf(w, `<rect x="%f" y="%f" width="%f" height="%f"/>`, rect.Location.X, rect.Location.Y, rect.Size.Width, rect.Size.Height)
	return err
}

Every of them has a function that is responsible for their instantiation based on the provided Viewport. The Viewport is an argument which keeps an information about the location and the size of the view port.

type ShapeFactory interface {
	Create(viewport Viewport) Shape
}

The CircleFactory creates a Circle instance that has radius, which fits the viewport:

type CircleFactory struct{}

func (factory *CircleFactory) Create(viewport Viewport) Shape {
	return &Circle{
		Location: viewport.Location,
		Radius:   math.Min(viewport.Size.Width, viewport.Size.Height),
	}
}

The RectangleFactory produces a rectangle that fits the viewport:

type RactangleFactory struct{}

func (factory *RactangleFactory) Create(viewport Viewport) Shape {
	return &Rectangle{
		Location: viewport.Location,
		Size:     viewport.Size,
	}
}

The main object Document has a Draw function, which composes a different shapes created by provided factories. The Document can be instaciated with different set of factories. This allow to customize and change the document’s content:

type Document struct {
	ShapeFactories []ShapeFactory
}

func (doc *Document) Draw(w io.Writer) error {
	viewport := Viewport{
		Location: Point{
			X: 0,
			Y: 0,
		},
		Size: Size{
			Width:  640,
			Height: 480,
		},
	}
	if _, err := fmt.Fprintf(w, `<svg height="%f" width="%f">`, viewport.Size.Height, viewport.Size.Width); err != nil {
		return err
	}

	for _, factory := range doc.ShapeFactories {
		shape := factory.Create(viewport)
		if err := shape.Draw(w); err != nil {
			return err
		}
	}

	_, err := fmt.Fprint(w, `</svg>`)
	return err
}

We should instaciate the Document struct with the available factories in the following way:

doc := &svg.Document{
	ShapeFactories: []svg.ShapeFactory{
		&svg.CircleFactory{},
		&svg.RactangleFactory{},
	},
}

doc.Draw(os.Stdout)

You can get the full source code from github.

Important aspects when we implement the Factory Method design pattern are:

Verdict

The Factory Method is one of the most used design patterns. It makes a design more customizable and only a little more complicated. Other design patterns require new structs, whereas Factory Method only requires a new operation. The Factory Method is similar to Abstract Factory but without the emphasis on families.