Jonathan Boccara's blog

Design Patterns VS Design Principles: Abstract Factory

Published April 6, 2022 - 0 Comments

In the “Design Pattens VS Design Principles” series, we look at design patterns and relate them to design principles. In this episode, we examine the Abstract Factory pattern.

Let’s see how Abstract Factory works and what it is useful for, then relate it to a design principle. We will also see a C++ technique to implement Abstract Factory with classes nested in a function.

Design Patterns and Design Principles

What is the difference between design patterns and design principles?

The design patterns we talk about are the collection of patterns described in the popular GoF book:

design patterns book GoF

Design patterns are typical ways to organise the components of a program in typical situations.

Design principles, on the other hand, are general guidelines te help create robust designs. The 9 GRASP principles are described in Craig Larman’s Applying UML and Patterns book:

Applying UML and Patterns

The 9 GRASP design principles are:

  • Low Coupling
  • High cohesion
  • Creator
  • Information expert
  • Polymorphism
  • Indirection
  • Protected Variations
  • Pure Fabrication
  • Controller

Let’s analyse the GoF design pattern Abstract Factory, and decide to which GRASP principle it relates the most.

What Abstract Factory is useful for

The goal of the Abstract Factory pattern is to make sure that objects are created consistently with each other.

This need arises when several objects can be created in a context, and there are several ways to create those objects. The GoF books uses the term of “family”: there are several families of objects. You can also see it as if there were several brands of objects, and in a given context you want all objects to be created of the same brand.

To do that, the Abstract Factory pattern consists in assigning to one class the responsibility of creating all the objects of a given family or brand.

Concrete factories

Let’s illustrate with a simple example, with an Abstract Factory that builds the parts of a house. In practice, the objects we create in code are more abstract (I’ve used Abstract Factory to create objects related to transactions for example), but let’s use this simple example to illustrate the structure of the pattern.

The objects we want to create are a door, a roof and a wall. There are several types of houses: blue houses, red houses, and yellow houses. And there may be more of them.

When we build a blue house, we want to make sure that all its components are blue. If we were to instantiate each object separately, each time there would be a risk of not instantiating the right type.

To mitigate that risk, we instantiate only one object: the blue house factory. Then we only use this object to create the others. This means that if we get the right factory, we’re guaranteed to get all the components of the right color too.

BlueHouseFactory
{
    Door createDoor() const; // this returns a blue door
    Roof createRoof() const; // this returns a blue roof
    Wall createWall() const; // this returns a blue wall
};

Note that this is a simple factory that doesn’t use polymorphism on the objects it creates (Door, etc.). Indeed, there could also be polymorphism in the objects that the factory creates:

class BlueHouseFactory
{
public:
    std::unique_ptr<Door> createDoor() const; // this returns a blue door
    std::unique_ptr<Roof> createRoof() const; // this returns a blue roof
    std::unique_ptr<Wall> createWall() const; // this returns a blue wall
};

With subclasses for each object:

class BlueDoor : public Door
{
    // ...
};

And the code for createDoor would then look like this:

std::unique_ptr<Door> BlueHouseFactory::createDoor()
{
    return std::make_unique<BlueDoor>();
}

Abstract Factory

Now we have one factory, that creates blue components. We go on and introduce other factories, that create other types of components: the RedHouseFactory and the YellowHouseFactory.

The goal is to use only one factory in a given context. To do that, we introduce an abstract factory, that can be either one of the above factories:

class HouseFactory
{
public:
    virtual std::unique_ptr<Door> createDoor() const = 0;
    virtual std::unique_ptr<Roof> createRoof() const = 0;
    virtual std::unique_ptr<Wall> createWall() const = 0;
    virtual ~HouseFactory() = 0;
};

The concrete factories then implement this interface:

class BlueHouseFactory : public HouseFactory
{
public:
    std::unique_ptr<Door> createDoor() const override; // this returns a blue door
    std::unique_ptr<Roof> createRoof() const override; // this returns a blue roof
    std::unique_ptr<Wall> createWall() const override; // this returns a blue wall
};

class RedHouseFactory : public HouseFactory
{
public:
    std::unique_ptr<Door> createDoor() const override; // this returns a red door
    std::unique_ptr<Roof> createRoof() const override; // this returns a red roof
    std::unique_ptr<Wall> createWall() const override; // this returns a red wall
};

class YellowHouseFactory : public HouseFactory
{
public:
    std::unique_ptr<Door> createDoor() const override; // this returns a yellow door
    std::unique_ptr<Roof> createRoof() const override; // this returns a yellow roof
    std::unique_ptr<Wall> createWall() const override; // this returns a yellow wall
};

Creating the factory

The factory is made to create objects, but who creates the factory?

There are various ways to do that. One of them is to instantiate various concrete factories in various places of the code, depending on the context. Given a function that uses a factory:

House buildAHouse(HouseFactory const& houseFactory);

We could call this fonction with a concrete factory if we know which one to use:

auto const blueHouse = buildAHouse(BlueHouseFactory{});

Another option is to centralise the creation of the factory, in a function (that happens to follow another design pattern, Factory Method, which we’ll explore in another post):

std::unique_ptr<HouseFactory> createHouseFactory(Color color)
{
    switch (color)
    {
        case Color::Blue:
        {
            return std::unique_ptr<BlueHouseFactory>{};
            break;
        }
        case Color::Red:
        {
            return std::unique_ptr<RedHouseFactory>{};
            break;
        }
        case Color::Yellow:
        default:
        {
            return std::unique_ptr<YellowHouseFactory>{};
            break;
        }
    }
}

This has the drawback of having to deal with the default case (Should we have a default color as in the above code? Should we produce an error? Or return a nullptr that we test everywhere?).

If you centralise the creation of the factory, know that C++ gives you the option to centralise the definitions of the factories along with them, by using classes nested in a function:

std::unique_ptr<HouseFactory> createHouseFactory(Color color)
{
    switch (color)
    {
        case Color::Blue:
        {
            class BlueHouseFactory : public HouseFactory
            {
            public:
                std::unique_ptr<Door> createDoor() const override { return std::make_unique<BlueDoor>(); };
                std::unique_ptr<Roof> createRoof() const override { return std::make_unique<BlueRoof>(); };
                std::unique_ptr<Wall> createWall() const override { return std::make_unique<BlueWall>(); };
            };

            return std::unique_ptr<BlueHouseFactory>{};
            break;
        }

        case Color::Red:
        {
            class RedHouseFactory : public HouseFactory
            {
            public:
                std::unique_ptr<Door> createDoor() const override { return std::make_unique<RedDoor>(); };
                std::unique_ptr<Roof> createRoof() const override { return std::make_unique<RedRoof>(); };
                std::unique_ptr<Wall> createWall() const override { return std::make_unique<RedWall>(); };
            };

            return std::unique_ptr<RedHouseFactory>{};
            break;
        }

        case Color::Yellow:
        default:
        {
            class YellowHouseFactory : public HouseFactory
            {
            public:
                std::unique_ptr<Door> createDoor() const override { return std::make_unique<YellowDoor>(); };
                std::unique_ptr<Roof> createRoof() const override { return std::make_unique<YellowRoof>(); };
                std::unique_ptr<Wall> createWall() const override { return std::make_unique<YellowWall>(); };
            };

            return std::unique_ptr<YellowHouseFactory>{};
            break;
        }
    }
}

An advantage of this is that all the factory code is located together, and you’re guaranteed that no other code can instantiate a BlueHouseFactory, because they don’t have access to that class.

But as a drawback, it makes the factory creation fonction bigger. If the implementations of the factories are not very simple, this makes the code hard to follow.

Design principles

The role of Abstract Factory is to present an interface for creating objects, that is implemented by various concrete factories. For this reason, I think Abstract Factory implements the Polymorphism GRASP design principle.

Also, the factory often doesn’t model a domain object. It is a technical helper to centralise the creation of consistent objects. This also makes it a Pure Fabrication.

Doesn’t Abstract Factory also implement the Creator GRASP design principle? Given its name, this is what I would have thought before digging in the analysis.

But if we go back to the definition of Creator, it suggests that B should create A if B is close to A: if it uses A, if it has inputs to create A, if it already contains other instances of A, etc. This does not match the description of Abstract Factory as well as Polymorphism does.

Would you also have reacted Abstract Factory to Polymorphism and Pure Fabrication, or to another one?

Do you know of creational patterns that are not in the GoF book?

Let me know by leaving a comment below.

You will also like

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin