Abstract Fac­to­ry is a cre­ation­al design pat­tern that lets you pro­duce fam­i­lies of relat­ed objects with­out spec­i­fy­ing their con­crete classes.

Problem: Imag­ine that you’re cre­at­ing a fur­ni­ture shop sim­u­la­tor. Your code con­sists of class­es that represent:

  1. A fam­i­ly of relat­ed prod­ucts, say: Chair + Sofa + CoffeeTable.
  2. Sev­er­al vari­ants of this fam­i­ly. For exam­ple, prod­ucts Chair + Sofa + CoffeeTable are avail­able in these vari­ants: Modern, Victorian, ArtDeco.”

You need a way to cre­ate indi­vid­ual fur­ni­ture objects so that they match other objects of the same fam­i­ly. Cus­tomers get quite mad when they receive non-match­ing furniture.

image.png

Prod­uct fam­i­lies and their variants.

Prod­uct fam­i­lies and their variants.

Solution: Explic­it­ly declare inter­faces for each dis­tinct prod­uct of the prod­uct fam­i­ly (e.g., chair, sofa or cof­fee table). Then you can make all vari­ants of prod­ucts fol­low those inter­faces. For exam­ple, all chair vari­ants can imple­ment the Chair inter­face; all cof­fee table vari­ants can imple­ment the CoffeeTable inter­face, and so on.

All variants of the same object must be moved to a single class hierarchy.

All variants of the same object must be moved to a single class hierarchy.

The next move is to declare the Abstract Fac­to­ry—an inter­face with a list of cre­ation meth­ods for all prod­ucts that are part of the prod­uct fam­i­ly (for exam­ple, createChair, createSofa and createCoffeeTable). These meth­ods must return abstract prod­uct types rep­re­sent­ed by the inter­faces we extract­ed pre­vi­ous­ly: Chair, Sofa, CoffeeTable and so on.

Each concrete factory corresponds to a specific product variant

Each concrete factory corresponds to a specific product variant

For each vari­ant of a prod­uct fam­i­ly, we cre­ate a sep­a­rate fac­to­ry class based on the AbstractFactory inter­face. A fac­to­ry is a class that returns prod­ucts of a par­tic­u­lar kind. For exam­ple, the ModernFurnitureFactory can only cre­ate ModernChair, ModernSofa and ModernCoffeeTable objects.

<aside>

Applicability

The Abstract Fac­to­ry pro­vides you with an inter­face for cre­at­ing objects from each class of the prod­uct fam­i­ly. As long as your code cre­ates objects via this inter­face, you don’t have to worry about cre­at­ing the wrong vari­ant of a prod­uct which doesn’t match the prod­ucts already cre­at­ed by your app.

Structure

image.png

  1. Abstract Prod­ucts declare inter­faces for a set of dis­tinct but relat­ed prod­ucts which make up a prod­uct family.

  2. Con­crete Prod­ucts are var­i­ous imple­men­ta­tions of abstract prod­ucts, grouped by vari­ants. Each abstract prod­uct (chair/sofa) must be imple­ment­ed in all given vari­ants (Vic­to­ri­an/Mod­ern).

  3. The Abstract Fac­to­ry inter­face declares a set of meth­ods for cre­at­ing each of the abstract products.

  4. Con­crete Fac­to­ries imple­ment cre­ation meth­ods of the abstract fac­to­ry. Each con­crete fac­to­ry cor­re­sponds to a spe­cif­ic vari­ant of prod­ucts and cre­ates only those prod­uct variants.

  5. Although con­crete fac­to­ries instan­ti­ate con­crete prod­ucts, sig­na­tures of their cre­ation meth­ods must return cor­re­spond­ing abstract prod­ucts. This way the client code that uses a fac­to­ry doesn’t get cou­pled to the spe­cif­ic vari­ant of the prod­uct it gets from a fac­to­ry.

    The Client can work with any con­crete fac­to­ry/prod­uct vari­ant, as long as it com­mu­ni­cates with their objects via abstract interfaces.

<aside>

How to Implement

  1. Map out a matrix of dis­tinct prod­uct types ver­sus vari­ants of these products.
  2. Declare abstract prod­uct inter­faces for all prod­uct types. Then make all con­crete prod­uct class­es imple­ment these interfaces.
  3. Declare the abstract fac­to­ry inter­face with a set of cre­ation meth­ods for all abstract products.
  4. Imple­ment a set of con­crete fac­to­ry class­es, one for each prod­uct variant.
  5. Cre­ate fac­to­ry ini­tial­iza­tion code some­where in the app. It should instan­ti­ate one of the con­crete fac­to­ry class­es, depend­ing on the appli­ca­tion con­fig­u­ra­tion or the cur­rent envi­ron­ment. Pass this fac­to­ry object to all class­es that con­struct products.
  6. Scan through the code and find all direct calls to prod­uct con­struc­tors. Replace them with calls to the appro­pri­ate cre­ation method on the fac­to­ry object. </aside>

Pseudocode

The cross-platform UI classes example

The cross-platform UI classes example

This exam­ple illus­trates how the Abstract Fac­to­ry pat­tern can be used for cre­at­ing cross-plat­form UI ele­ments with­out cou­pling the client code to con­crete UI class­es, while keep­ing all cre­at­ed ele­ments con­sis­tent with a select­ed oper­at­ing system.

The same UI ele­ments in a cross-plat­form appli­ca­tion are expect­ed to behave sim­i­lar­ly, but look a lit­tle bit dif­fer­ent under dif­fer­ent oper­at­ing sys­tems. More­over, it’s your job to make sure that the UI ele­ments match the style of the cur­rent oper­at­ing sys­tem. You wouldn’t want your pro­gram to ren­der macOS con­trols when it’s exe­cut­ed in Windows.

The Abstract Fac­to­ry inter­face declares a set of cre­ation meth­ods that the client code can use to pro­duce dif­fer­ent types of UI ele­ments. Con­crete fac­to­ries cor­re­spond to spe­cif­ic oper­at­ing sys­tems and cre­ate the UI ele­ments that match that par­tic­u­lar OS. It works like this: when an appli­ca­tion launch­es, it checks the type of the cur­rent oper­at­ing sys­tem. The app uses this infor­ma­tion to cre­ate a fac­to­ry object from a class that match­es the oper­at­ing sys­tem. The rest of the code uses this fac­to­ry to cre­ate UI ele­ments. This pre­vents the wrong ele­ments from being created. With this approach, the client code doesn’t depend on con­crete class­es of fac­to­ries and UI ele­ments as long as it works with these objects via their abstract inter­faces. This also lets the client code sup­port other fac­to­ries or UI ele­ments that you might add in the future. As a result, you don’t need to mod­i­fy the client code each time you add a new vari­a­tion of UI ele­ments to your app. You just have to cre­ate a new fac­to­ry class that pro­duces these ele­ments and slight­ly mod­i­fy the app’s ini­tial­iza­tion code so it selects that class when appropriate.

// The abstract factory interface declares a set of methods that
// return different abstract products. These products are called
// a family and are related by a high-level theme or concept.
// Products of one family are usually able to collaborate among
// themselves. A family of products may have several variants,
// but the products of one variant are incompatible with the
// products of another variant.
interface GUIFactory is
  method createButton():Button
  method createCheckbox():Checkbox

// Concrete factories produce a family of products that belong
// to a single variant. The factory guarantees that the
// resulting products are compatible. Signatures of the concrete
// factory's methods return an abstract product, while inside
// the method a concrete product is instantiated.
class WinFactory implements GUIFactory is
  method createButton():Button is
    return new WinButton()
  method createCheckbox():Checkbox is
    return new WinCheckbox()

// Each concrete factory has a corresponding product variant.
class MacFactory implements GUIFactory is
  method createButton():Button is
    return new MacButton()
  method createCheckbox():Checkbox is
    return new MacCheckbox()

// Each distinct product of a product family should have a base
// interface. All variants of the product must implement this
// interface.
interface Button is
  method paint()

// Concrete products are created by corresponding concrete
// factories.
class WinButton implements Button is
  method paint() is
    // Render a button in Windows style.

class MacButton implements Button is
  method paint() is
    // Render a button in macOS style.

// Here's the base interface of another product. All products
// can interact with each other, but proper interaction is
// possible only between products of the same concrete variant.
interface Checkbox is
  method paint()

class WinCheckbox implements Checkbox is
  method paint() is
    // Render a checkbox in Windows style.

class MacCheckbox implements Checkbox is
  method paint() is
    // Render a checkbox in macOS style.

// The client code works with factories and products only
// through abstract types: GUIFactory, Button and Checkbox. This
// lets you pass any factory or product subclass to the client
// code without breaking it.
class Application is
  private field factory: GUIFactory
  private field button: Button
  constructor Application(factory: GUIFactory) is
    this.factory = factory
  method createUI() is
    this.button = factory.createButton()
  method paint() is
    button.paint()

// The application picks the factory type depending on the
// current configuration or environment settings and creates it
// at runtime (usually at the initialization stage).
class ApplicationConfigurator is
  method main() is
    config = readApplicationConfigFile()

    if (config.OS == "Windows") then”
			factory = new WinFactory()
    else if (config.OS == "Mac") then
      factory = new MacFactory()
    else
      throw new Exception("Error! Unknown operating system.")

    Application app = new Application(factory)