intentions

Provides an interface for creating families of related or dependent objects without specifying their concrete classes.

The alias

Kit

motivation

Suppose a user interface toolkit supports multiple “look and feel” standards, such as theme and presentation management. Different “look and feel” interfaces for user defined “widgets” such as scrollbars, Windows, and buttons have different looks and behaviors. To facilitate migration to the “look and feel” standard, applications should not hardcode widgets for a specific look and feel. Instantiating the “specific look and feel” widget class consistently makes it difficult for applications to change the look and feel later.

We can solve this problem by defining an abstract widget factory. The widget factory declares an interface for creating each base widget. Abstract classes are also not for each type of widget, concrete subclasses implement a specific look and feel of the widget. The widget factory interface has an operation that returns a new widget object for each abstract widget class. The user calls these operations to get the widget instance, but the user doesn’t care about the concrete class they use. So the user and the popular look and feel remain independent.

Above is a concrete subclass about the widget factory that is standard for each look and feel. Each subclass implements actions to create widgets with the appropriate look and feel. Each subclass implements the action of creating the appropriate widget for the “look and feel.” For example, the CreateScrollBar operation in the ModifWidgetFactory instance returns a Motif scrollbar for display management. Users simply create widgets through WidgetFactory’s interface, and don’t worry about classes and implementing widgets that are realistic for specific behavior and feel. In other words, the user only needs to submit an interface definition through the abstract class, not the concrete class.

A WidgetFacory also enforces dependencies between specific widget classes. A Motif scrollbar should be edited with the Motif button and motif text, a constraint that is automatically enforced as a result using a MotifWidgetFactory.

application

When to use the Abstract factory pattern:

  • A system should be independent of how products are created, assembled, and presented.
  • A system should be configurable using one of many product families.
  • A family of related product objects is designed to be used together, and you need to enforce this constraint.
  • You want to provide a library of products, and you want to look only for their interfaces, not their implementations.

structure

participants

  • The abstract factory
    • Declares an interface for creating abstract product object operations
  • The specific factory
    • Implement the actions used to create concrete product objects
  • Abstract product
    • Declares an interface for a class of product objects
  • Specific products
    • Defines a product object created by the corresponding concrete factory
    • Implement abstract product interfaces
  • The customer
    • Use only the interfaces of abstract factories and abstract product classes

collaboration

  • Typically a separate instance of a concrete factory class is created at run time. Specific factories create objects with specific implementations. To create different product objects, users need to use different concrete factories.
  • An abstract factory delays the creation of an object into its concrete factory subclass.

impact

The abstract factory pattern has the following benefits and liabilities:

  • Concrete classes are isolated. The abstract factory pattern helps you control the classes of objects your application creates. Because a factory encapsulates the responsibility and process for creating product objects, it isolates consumer and implementation classes. Consumers manipulate instances through abstract interfaces. The product class name is isolated in the implementation of the specific factory and does not appear in the consumer’s code.
  • Makes it easy to change product families. A concrete factory class appears only once in an application, when it is instantiated. This makes it easy to change the specific factory an application uses. Different product configurations can be used by changing specific factories. Because an abstract factory creates an entire product family, the entire product family changes at once. In our user interface example, we can switch from the Motif widget to the PresentationManager widget by simply reconstructing the instance by switching the corresponding factory object.
  • Promote consistency across products. We produce objects in a family that is designed to work together, and it is important for an application to use objects from one family at a particular time. Abstract factories make it easy to follow.
  • It’s hard to support new kinds of products. Expanding abstract factories to produce new kinds of products is not easy. Because the abstract factory interface fixes the set of products that can be created. Assuming that a new class of products needs to extend the factory interface results in changing the abstract factory class and all of its subclasses. We discuss one of these solutions in the implementation section.

implementation

There are some useful techniques for implementing the abstract factory pattern.

  1. Factory as a singleton. An application typically requires only one instance of a specific factory for each product family. So it’s usually best implemented as a singleton.
  2. Create the product. The abstract factory simply declares an interface for creating objects. You don’t actually create them until you create product subclasses. The most common approach is to define a factory method for each product. A specific factory will specify the products it produces by overriding each factory method. At the same time, the implementation is simple, requiring a new concrete factory subclass for each product family, even if the product family is only slightly different.

If there are many product families, the specific factory can be implemented using the Prototype pattern. Concrete factory usage is initialized with a prototype instance for each product in the family, which then creates a new product by cloning its prototype. This prototype-based approach eliminates the need for a new specific factory class for each new product family.

There is a way to implement a prototype-based factory in Smalltalk. The specific factory stores the prototypes to be cloned in a directory called partCatalog. This method enables: retrieving the prototype and cloning it:

make: partName
       ^ (partCatalog at: partName) copy
The concrete factory has a method for adding parts to the catalog.
   addPart: partTemplate named: partName
       partCatalog at: partName put: partTemplate
Prototypes are added to the factory by identifying them with a symbol: aFactory addPart: aPrototype named: #ACMEWidget

Copy the code

A variation on the prototype-based approach is that it is possible to treat classes as first class objects in a language (e.g., Smalltalk and ObjectiveC). You can think of a class as a degenerate factory in these languages, creating only one class of objects. You can store classes in a concrete factory that produces various products that are concrete in variables, like prototypes. You define a new factory by initializing an instance of a concrete factory class, through the product class rather than its subclasses. This approach has the benefit of language characteristics, whereas a purely prototype-based approach is language independent.

Like the prototype-based factory in Smalltalk just discussed, the class-based version will have a separate instance variable, partCataglog, which is a dictionary whose key values are the names of the parts. Instead of storing prototypes to be copied, partCataglog stores classes for the product. The method looks like this:

make: partName
^ (partCatalog at: partName) new
Copy the code
  1. Define extensible factories. Abstract factories typically define different operations for each type of product that can be produced. The product category code is on the operation signature. Adding new product categories requires changing the abstraction of the common interfaces and the classes that depend on them.

A more flexible but less secure design is to add parameters to the operation that creates the object. This parameter specifies the type of object to be created. It can be a class representation, an integer, a string of any type that identifies the product category. In practice, the abstract factory simply requires a “make” operation with an argument specifying the type of object to be created. This is the abstract factory discussed earlier for both type-based and object-based techniques.

This variant is easier for dynamically typed languages like smalltalk than for statically typed languages like C++. You can use this in C++ only if all objects have the same abstract base class or when product objects can be safely created to be of the correct type by requesting them from the client. The factory methods in the implementation section show how to implement these parameterized operations in C++.

But even when there is no coercion, there is an inheritance problem: all products are returned to the client using the same abstract factory interface of type return value type. Clients will not be able to differentiate or safely assume viewing leaf products. If clients need to perform operations specified by subclasses, they cannot be centralized through the abstract factory interface. Although clients can perform downconversions (such as dynamic conversions in C++), it is not achievable or safe because downconversions may fail. This is typically an exchange for a highly flexible and extensible interface.

The sample code

We will apply the abstract factory pattern to the creation maze we discussed in the opening section.

class MazeFactory { public: MazeFactory(); virtual Maze* MakeMaze() const { return new Maze; } virtual Wall* MakeWall() const { return new Wall; } virtual Room* MakeRoom(int n) const { return new Room(n); } virtual Door* MakeDoor(Room* r1, Room* r2) const { return new Door(r1, r2); }};Copy the code
Maze* MazeGame::CreateMaze (MazeFactory& factory) {
       Maze* aMaze = factory.MakeMaze();
       Room* r1 = factory.MakeRoom(1);
       Room* r2 = factory.MakeRoom(2);
       Door* aDoor = factory.MakeDoor(r1, r2);
       aMaze->AddRoom(r1);
       aMaze->AddRoom(r2);
       r1->SetSide(North, factory.MakeWall());
       r1->SetSide(East, aDoor);
       r1->SetSide(South, factory.MakeWall());
       r1->SetSide(West, factory.MakeWall());
       r2->SetSide(North, factory.MakeWall());
       r2->SetSide(East, factory.MakeWall());
       r2->SetSide(South, factory.MakeWall());
       r2->SetSide(West, aDoor);
       return aMaze;
   }
   
Copy the code
class EnchantedMazeFactory : public MazeFactory {
   public:
       EnchantedMazeFactory();
virtual Room* MakeRoom(int n) const
{ return new EnchantedRoom(n, CastSpell()); }
virtual Door* MakeDoor(Room* r1, Room* r2) const { return new DoorNeedingSpell(r1, r2); }
   protected:
       Spell* CastSpell() const;
};

Copy the code

Known application

‘InterViews’ uses the’ Kit ‘suffix to denote abstract factory classes. WidgetKit and DialogKit abstraction factories are defined to generate “look-and-feel-specific” user interface objects. ‘InterViews’ also includes a LayoutKit that generates different composite objects depending on the layout design. For example, a layout is conceptually horizontal and may require different combination objects, depending on the document orientation (landscape or portrait).

Related patterns

Abstract factory patterns are often implemented in a factory fashion, but can also be implemented in a prototype pattern.

A specific plant is usually a singleton.