When to use it: Abstract Factory pattern

Let’s discuss the Abstract Factory pattern to continue our tour of design patterns in C++ .  This is an interesting technique for maintaining coherence between objects, while allowing easy switching or swapping of object sets.  This can be useful for allowing an application to use multiple UI toolkits (Apple vs. Windows), allowing different enemies with different capabilities in a game, using different submodels in a complicated simulation, and much more.

Factories

In the real world, a factory is a building that produces one or more products.  A steel factory might produce beams & sheets, while a shoe factory might produce all the colorways & sizes for both a running and a tennis shoe.

In the software world, a factory is a function or method that creates object instances.  For example, a game might use an Enemy factory to randomly produce a GroundEnemy or a FlyingEnemy.  Its signature might look something like this:

enum EnemyType { GroundEnemyType, FlyingEnemyType };
class Enemy {
public:
virtual void move(Direction d) = 0;
virtual void attack() = 0;
};
class GroundEnemy : Enemy { … };
class FlyingEnemy : Enemy { … };

Enemy* CreateEnemy(EnemyType et);

 

Notice that the factory returns a pointer to an Enemy, which is a parent class of both GroundEnemy and FlyingEnemy. The subclasses would implement the pure virtual functions move() & attack() to implement subclass-specific actions. So CreateEnemy can be used by client code to produce either of the subclasses, and then use the common interface to manipulate them.

Factory for Factory

Now, we can imagine that each kind of Enemy has its own kind of weapon that shoots differently behaving Projectiles: bullets from a pistol and bombs from a carpet bomb. Let’s assume that only ground enemies can use pistols, and only flying enemies can use carpet bombs. How can the client create Projectiles that match with the Enemy?

This is the problem that the Abstract Factory attempts to solve. It does so by putting a factory in the object created by a factory.

class Projectile {
public:

virtual int getDamage() = 0;
virtual float getInitialHorizontalVelocity() = 0;

};

class PistolProjectile : Projectile { … };
class CarpetBombProjectile : Projectile { … };

class Enemy {
public:

virtual Projectile* createProjectile() = 0;

};

class GroundEnemy : Enemy {
public:

virtual Projectile* createProjectile() { return new PistolProjectile(); }

};
class FlyingEnemy : Enemy {
public:

virtual Projectile* createProjectile() { return new CarpetBombProjectile(); }

};

The client creates a Projectile by calling the factory method createProjectile() in the Enemy class.

So to recap: client code might want to randomly get a ground or flying enemy. Then, the client code wants to shoot the enemy’s weapon and track the projectile’s path. To do so, we could create a factory method that returns either a GroundEnemy or a FlyingEnemy. In both classes, we implement createProjectile() so that it returns a new instance of the Projectile that the enemy uses. This allows the client to use one code path to simply create an Enemy, then create a matching Projectile, and ensure that no Projectiles are used with the wrong Enemy. Thus, we’ve maintained object coherence.

Drawback

This is all nice when you want to deal with multiple sets of coherent objects that have the same interface.  But what if they don’t have the same interface, ie inconsistent object set interfaces?

For example, let’s say you want to support 2 fictional UI toolkits, UIFrameworkForWindows and UIFrameworkForMac.  These toolkits will allow your app to have a more or less native appearance on both Windows and Mac.  They both support Windows, Scrollbars, and all the common UI elements.  Thus, you can create an interface for your Abstract Factory that has methods like CreateWindow, CreateScrollbar, and so on.

But UIFrameworkForWindows also has a RightButtonContextMenu class, which you would like to use because you feel it will be a valuable addition to the UI on the Windows platform.  However, there’s no corresponding class in the UIFrameworkForWindows.  What should the Abstract Factory interface look like?  If you add a CreateRightButtonContextMenu method, the UIFrameworkForMac wrapper will need to implement it, perhaps as an empty method.  This feels like a workaround and not “clean code.”  If you don’t the CreateRightButtonContextMenu method, then client code will need to detect the platform that is being used, and only call CreateRightButtonContextMenu when running on Windows.  This eliminates much of the value of the Abstract Factory pattern, which promises to allow client code a single interface to any of the many underlying object sets.

When to use it

The Abstract Factory pattern can be used when you have two object sets with very similar, if not the same, interfaces, but each set needs to be used coherently (those objects only work with other objects from the set, and not from a different set).  This occurs in UI toolkits, games, and many more domains.  It allows client code to get a handle to a factory that contains factory methods for creating coherent objects.  As long as all the objects are created using the Abstract Factory, there is no chance that client code will attempt to create (for example) a MacWindow with a WindowsScrollbar.

Please leave any comments below, I’d love to hear any feedback on other situations where the Abstract Factory pattern is used, as well as any other drawbacks!

Leave a Reply

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