Creational Patterns  «Prev  Next»
Lesson 5Factory Method: motivation
ObjectiveWrite an abstract Vehicle Class

Write an abstract Vehicle Class

Polymorphism is a powerful tool that uses subclasses as instances of their common superclass. For instance, an abstract base class called Vehicle may be a Car, a Bicycle, or a Bus. Each of these subclasses behaves differently (for example, a Bus does not accelerate as fast as a Car) but they all support the same Vehicle interface; that is, they all have public start(), stop(), and accelerate() methods. Many times they may not have any other public members than precisely those declared in the common superclass. This allows client classes to be written without detailed knowledge of exactly which class they are working with. The same code that works with a Bus works with a Car, with a Motorcycle, with a Bicycle, and so forth.

Factory Method: Motivation

In, C++ destructors are also an exception. Furthermore, in C++ a destructor is an operation that is automatically invoked to finalize an object that is about to be deleted. Destructors must have the same name as the name of the class prefixed with a tilde (~). Thus clients must know the name of the destructor as well. Most of the time, you will just use the default destructor so this is not an issue. If it is an issue for your code, it is not hard to convert the Factory Method or Abstract Factory pattern to work with destructors as well as constructors. In fact you can use one of these Factory patterns for the destructors, while still using another pattern like Builder or Prototype for the creation process. In practice, you only rarely need to do this.
The factory method pattern is an object-oriented creational design pattern to implement the concept of factories and deals with the problem of creating objects without specifying the exact class of object that will be created. The essence of this pattern is to define an interface for creating an object and allow the classes that implement the interface decide which class to instantiate. The Factory method lets a class defer instantiation to subclasses.
Creating an object often requires complex processes not appropriate to include within a composing object. The object's creation may lead to a significant duplication of code and may require information not accessible to the composing object. This may not provide a sufficient level of abstraction or may otherwise not be part of the composing object's concerns. The factory method design pattern handles these problems by defining a separate method for creating the objects, which subclasses can then override to specify the derived type of product that will be created.

The Imperative of Object Factories in Creational Design Patterns

In the expansive realm of software design, creational design patterns stand as foundational blueprints that deal explicitly with object creation mechanisms. These patterns aim to create objects in a manner suitable to the situation while simultaneously keeping the system decoupled from the specific classes that it instantiates. Among these creational patterns, the concept of 'Object Factories' emerges as a pivotal strategy. Delving deeper, let's elucidate when and why Object Factories become indispensable within this context.
  1. Complex Object Creation:
    • When an object requires a multifaceted initialization process or involves multiple steps in its construction, an object factory proves beneficial. It encapsulates the complexity of this process, presenting a simplified interface for object creation.
  2. Decoupling System from Specific Classes:
    • In scenarios where a system needs to remain agnostic about the specific classes of objects it creates, object factories act as intermediaries. By centralizing the instantiation process, they reduce direct dependencies between concrete classes and the parts of the system requiring those objects.
  3. Variability in Created Objects:
    • When a system necessitates the creation of objects from various classes based on certain conditions or configurations, object factories come to the fore. They determine which class to instantiate, allowing for flexibility and variability in object creation.
  4. Reusability and Configuration:
    • Object factories bolster the reusability of a system. By externalizing the object creation process, these factories can be configured or extended independently, without necessitating changes to the overarching system or its other components.
  5. Lifecycle Management:
    • In situations where objects have a specific lifecycle or require pooling (reusing objects instead of creating new ones continually), object factories can manage this lifecycle, ensuring efficient resource utilization.
  6. Encapsulation of Construction Logic:
    • Often, the logic behind which object to instantiate or how to set up an object might be complex and subject to change. Object factories encapsulate this logic, ensuring that the system remains decoupled from these intricacies and is more maintainable.
  7. Consistent Object State:
    • Ensuring that objects are created in a consistent, valid state is paramount. Object factories can set default values, perform necessary validations, or undertake preparatory steps to guarantee that every object created is in a ready-to-use state.
In essence, while creational design patterns offer a suite of strategies for object instantiation, object factories distinctly shine in scenarios demanding abstraction of the creation process, variability in instantiation, or intricate initialization logic. Their introduction into a system not only enhances flexibility and decoupling but also ensures that object creation adheres to principles of consistency, reusability, and maintainability. Thus, in the architectural tapestry of software design, object factories are not mere conveniences but often critical cogs in the machinery.

Need for Object Factories

There are two basic cases in which object factories are needed. The first occurs when a library needs not only to manipulate user-defined objects, but also to create them. For example, imagine you develop a framework for multiwindow document editors. Because you want the framework to be easily extensible, you provide an abstract class Document from which users can derive classes such as TextDocument and HTMLDocument. Another framework component may be a DocumentManager class that keeps the list of all open documents. A good rule to introduce is that each document that exists in the application should be known by the DocumentManager[1]. Therefore, creating a new document is tightly coupled with adding it to DocumentManager's list of documents. When two operations are so coupled, it is best to put them in the same function and never perform them separately:

class DocumentManager
{
// public 
public:
Document* NewDocument();
private:
virtual Document* CreateDocument() = 0;
std::list<Document*> listOfDocs_;
};
Document* DocumentManager::NewDocument()
{
Document* pDoc = CreateDocument();
listOfDocs_.push_back(pDoc);
...
return pDoc;
}

The CreateDocument member function replaces a call to new. NewDocument cannot use the new operator because the concrete document to be created is not known by the time DocumentManager is written. In order to use the framework, programmers will derive from DocumentManager and override the virtual member function CreateDocument (which is likely to be pure). The GoF book calls CreateDocument a factory method. Because the derived class knows exactly the type of document to be created, it can invoke the new operator directly. This way, you can remove the type knowledge from the framework and have the framework operate on the base class Document only. The override is very simple and consists essentially of a call to new; for example:

Document* GraphicDocumentManager::CreateDocument()
{
  return new GraphicDocument;
}

Alternatively, an application built with this framework might support creation of multiple document types (for instance, 1) bitmapped graphics and 2) vectorized graphics). In that case, the overridden CreateDocument function might display a dialog to the user asking for the specific type of document to be created. Thinking of opening a document previously saved on disk in the framework just outlined brings us to the second case in which an object factory may be needed. When you save an object to a file, you must save its actual type in the form of a string, an integral value, an identifier of some sort. Thus, although the type information exists, its form does not allow you to create C++ objects.
The general concept underlying this situation is the creation of objects whose type information is genuinely postponed to runtime: entered by the end user, read from a persistent storage or network connection, or the like. Here the binding of types to values is pushed even further than in the case of polymorphism: When using polymorphism, the entity manipulating an object does not know its exact type; however, the object itself is of a well-determined type. When reading objects from some storage, the type comes alone at runtime. You must transform type information into an object. Finally, you must read the object from the storage, which is easy once an empty object is created, by invoking a virtual function. Creating objects from "pure" type information, and consequently adapting dynamic information to static C++ types, is an important issue in building object factories.
However, there is one exception: constructors. In Java and C++, constructors have the same name as their class. This means that a subclass cannot override its parent's constructor; and, more importantly, constructors must be invoked explicitly.
When a client instantiates a class with new, that client must know at compile time exactly which class it is instantiating.
This penetrates the layer of abstraction normally imposed by polymorphism and a common base class.
The Factory Method pattern is used in circumstances like this to remove specific knowledge of which class to instantiate from the client and instead place it inside the common base class and concrete subclasses where it belongs.

In particular, you should think of the Factory Method when any of the following conditions hold:
  1. You do not know at compile time which specific subclasss need to be instantiated.
  2. You want to defer the choice of which objects to create to a subclass.
  3. A class delegates its work to a helper class, and you want to remove explicit information about which class the work is delegated to.

Abstract VehicleFactory Class

public abstract class VehicleFactory {
 public static final String LUXURY_VEHICLE = "Luxury";
 public static final String NON_LUXURY_VEHICLE = "Non-Luxury";
 public abstract Car getCar();
 public abstract SUV getSUV();

}//End of class

Concrete Factory Subclasses of the Abstract VehicleFactory Class

public class LuxuryVehicleFactory extends VehicleFactory {
 public Car getCar() {
  return new LuxuryCar("L-C");
 }
 public SUV getSUV() {
  return new LuxurySUV("L-S");
 }
}//End of class

public class NonLuxuryVehicleFactory extends VehicleFactory {
 public Car getCar() {
  return new NonLuxuryCar("NL-C");
 }
 public SUV getSUV() {
  return new NonLuxurySUV("NL-S");
 }
}//End of class

Factory Method - Exercise

In this exercise, you will write a class that uses the Factory Method pattern.
Factory Method - Exercise

SEMrush Software