Abstract Factory

Abstract factory is to provide an interface to create family of related objects without specifying concrete classes. The idea is that the client supplies a set of concrete classes to the factory and then the factory "stamp" the products out. The analogy is like the sheet metal stamping machine. Once the client provides the blueprint for wheels, door, hood, etc., the machine stamps the wheels product, door product, hood product, etc out subsequently.

When to Use (Problem)

  • When the problem requires one to create a family set of related / dependent objects without specifying the concrete classes upfront.
  • A hierarchy that encapsulates: platform, operating system, construction of suites, etc.
  • When factory method is considered harmful.

Example (Solution)

Here is an example of abstract factory in Go, the ShapeFactory:

package main

import (
        "fmt"
)

const (
        CircleID    = 0
        SquareID    = 1
        EllipseID   = 2
        RectangleID = 3
)

// ShapeFactory is an abstract factory for all shapes. It does not have any
// shape data on its own. Rather, it only produces the shape when the client
// provides the necessary shape data.
type ShapeFactory struct {
        Circle    []byte
        Square    []byte
        Ellipse   []byte
        Rectangle []byte
}

func (s *ShapeFactory) CreateCurvedShape(shape int) []byte {
        switch shape {
        case 0:
                return s.Circle
        case 2:
                return s.Ellipse
        default:
                return nil
        }
}

func (s *ShapeFactory) CreateStraightShape(shape int) []byte {
        switch shape {
        case 1:
                return s.Square
        case 3:
                return s.Rectangle
        default:
                return nil
        }
}

func main() {
        // client supplies the models to the factory
        s := &ShapeFactory{
                Circle: []byte("I'm a circle"),
                Square: []byte("I'm a square"),
        }

        // not all shapes are printed out since client only supply 2 out 4
        // concrete shape data.
        fmt.Printf("Circle: %v\n", s.CreateCurvedShape(CircleID))
        fmt.Printf("Square: %v\n", s.CreateStraightShape(SquareID))
        fmt.Printf("Ellipse: %v\n", s.CreateCurvedShape(EllipseID))
        fmt.Printf("Rectangle: %v\n", s.CreateStraightShape(RectangleID))
}

Notice that ShapeFactory does not hold any fixed/concrete values for all 4 shapes at start. It requires client to supply all the shape data upon creation in main. However, instead of having all the shapes supplying its own data, ShapeFactory standardizes all shapes creation under 2 functions with ID: CreateCurvedShape, CreateStraightShape. That's how abstract factory works: it abstracts the function interfaces even at the absent of the concrete data.

Expected Outcome (Consequences)

  • A standardized creator interface is available.
  • A simpler organization of factory objects abstracts.