Software Development: The Design Pattern Factory Method of Creating Objects

Patterns are an important abstraction in modern software development. They offer well-defined terminology, clean documentation, and learning from the best. The classic book “Design Patterns: Elements of Reusable Object-Oriented Software” (Design Patterns for short) contains 23 patterns. They are classified according to their purpose: generational patterns, structural patterns and behavioral patterns. The factory method belongs to the former category of creating objects.




Rainer Grimm has worked as a software architect, team leader and training manager for many years. He likes to write articles on the programming languages ​​C++, Python and Haskell, but also often speaks at specialist conferences. On his blog Modernes C++, he deals intensively with his passion for C++.



Five patterns from the book “Design Patterns: Elements of Reusable Object-Oriented Software” are generative, seven are structural, and the rest are behavioral. What does it mean first of all?

  • generation patternr deal with creating objects in a well-defined way.
  • Structural patterns provide mechanisms for organizing classes and objects into larger structures.
  • behavior pattern deal with communication patterns between objects.

Before I start with the generational patterns, I want to make a quick disclaimer.

I present about half of the 23 patterns. For the rest, I only offer a profile. The selection of the presented patterns is based on two points:

  1. As a software developer, what patterns have I encountered most often over the past twenty years?
  2. Which patterns are still in use?

My explanation of the design patterns presented is deliberately brief. My idea is to introduce the key principle of a pattern and present it from C++’s point of view. If you want to know more details, you will find excellent documentation. Here are a few examples:

Creation patterns deal with the creation of objects.

I will write about two of the five generation patterns: factory method and singleton. I know, I know, the singleton could also be considered an anti-pattern. I will cover singleton in detail in a later post. I would like to start with the factory method.

Here are the facts:

The factory method defines an interface for creating a single object, but leaves it up to the subclasses to decide which objects to create. The interface can provide a default implementation for creating objects.

Virtual constructor

  • A class does not know what kind of objects to create
  • Subclasses determine which object to create
  • Classes delegate the creation of objects to subclasses

Each standard template library container has eight factory functions to create different iterators.

  • begin, cbegin: returns an iterator pointing to the beginning of the container
  • end, cend: returns an iterator pointing to the end of the container
  • begin, crbegin: returns a backward iterator pointing to the beginning of the container
  • rend, crend: returns a backward iterator pointing to the end of the container

The factory functions starting with c return constant iterators.



product

  • Objects of factoryMethod must be created.

specific product

  • Implements the interface

Creator

  • Declare the factory method
  • Calls the factory method

Concrete maker

  • Overrides the factory method

The creator does not instantiate the concrete product. He calls his virtual member function factoryMethod on. The concrete product is therefore created by the concrete creator, and the object creation is independent of the creator.

This pattern is also known as a virtual constructor.

To be honest, the name virtual constructor is misleading. There is no virtual constructor in C++, but we can use virtual constructor to simulate it.

A class hierarchy with an interface class serves as an example Window and two implementation classes DefaultWindow and FancyWindow.

// Product
class Window { 
 public: 
    virtual ~Window() {};
};

// Concrete Products 
class DefaultWindow: public Window {};

class FancyWindow: public Window {};

Now you want a new one Window create it on top of an already existing one Window based. That is, if you have an instance of DefaultWindow or FancyWindow in the factory mode getNewWindow used, it should return an instance of the same class.

The factory method is typically implemented with an enumeration and a factory function. Here is my first attempt:

The factory function in (1) determines from the input Window Which one Window (2 and 3) must be created. she uses window->getType() (4) to get the right one WindowType to research. Of WindowType is an enumeration.

Here is the output from the program:



Honestly, I don’t like this solution for the following reasons:

  1. If my application is new WindowTo support, I would need the enumeration WindowType and switch-Expand the instruction.
  2. that switch-The tutorial gets harder and harder to maintain as I add new ones WindowType add.
  3. The code is too complicated. This is primarily due to switch-Instruction.

That’s why I want to switch-Replace the instruction with a virtual dispatch. I want the existing ones too Windowclone p.

// factoryMethod.cpp

#include 

// Product
class Window{ 
 public: 
    virtual Window* create() = 0;                       // (1)
    virtual Window* clone() = 0;                        // (2)
    virtual ~Window() {};
};

// Concrete Products 
class DefaultWindow: public Window { 
    DefaultWindow* create() override { 
        std::cout << "Create DefaultWindow" << '\n';
        return new DefaultWindow();
    } 
     DefaultWindow* clone() override { 
        std::cout << "Clone DefaultWindow" << '\n';
        return new DefaultWindow(*this);
    } 
};

class FancyWindow: public Window { 
    FancyWindow* create() override { 
        std::cout << "Create FancyWindow" << '\n';
        return new FancyWindow();
    } 
    FancyWindow* clone() override { 
        std::cout << "Clone FancyWindow" << '\n';
        return new FancyWindow(*this);                  // (5)
    } 
};

// Concrete Creator or Client                             
Window* createWindow(Window& oldWindow) {               // (3)
    return oldWindow.create();
}

Window* cloneWindow(Window& oldWindow) {                // (4)    
    return oldWindow.clone();
}
  
int main() {

    std::cout << '\n';

    DefaultWindow defaultWindow;
    FancyWindow fancyWindow;
  
    const Window* defaultWindow1 = createWindow(defaultWindow);
    const Window* fancyWindow1 = createWindow(fancyWindow);
    
    const Window* defaultWindow2 = cloneWindow(defaultWindow);
    const Window* fancyWindow2 = cloneWindow(fancyWindow);
  
    delete defaultWindow1;
    delete fancyWindow1;
    delete defaultWindow2;
    delete fancyWindow2;

    std::cout << '\n';
  
}

The class Window now supports two new modes Windows to create: a standard-constructed Window with member function create(1) and a copied Window with the membership function clone (2). The subtle difference is that the constructor uses this-Points to the membership function clone (5) takes over. The factory is working createWindow (3) and cloneWindow (4) work with the dynamic type.

The results of the program are promising. Both membership functions create and clone specify the name of the object they are creating.



By the way: It's okay for the virtual member to work create and clone of DefaultWindow and Dec FancyWindow are private as they are about Windowinterface can be used. In the interface, both member functions are public.

Is my factory method fully implemented? NONE! The program factoryMethod.cpp has two serious problems: explicit ownership and carving. In my next article I will go into more detail on both points.


(rm)

To the home page

Leave a Comment