CreationalPatterns

The Factory Method

The classic book “Design Patterns: Elements of Reusable Object-Oriented Software” has 23 patterns. They are ordered by intent: creational, structural, and behavioral patterns. Today,  I focus on the creational pattern Factory Method.

 CreationalPatterns

Five patterns in the book “Design Patterns: Elements of Reusable Object-Oriented Software” (short Design Patterns) are creational, seven structural, and the remaining one behavioral. First of all, what does this mean?

  • Creational patterns deal with object creation in a well-defined way.
  • Structural patterns provide mechanisms to organize class and objects for larger structures.
  • Behavioral patterns deal with communication patterns between objects.

Before I start with the creational patterns,  I want to make a short disclaimer.

Short Disclaimer

I present about half of the 23 design patterns. For the remaining ones, I only provide a fact sheet. The selection of the presented design pattern is based on two points.

  1. Which patterns did I encounter most often as a software developer in the last twenty years?
  2. Which patterns are still in use?

My explanation of the presented design patterns is intentionally concise. My idea is to present the key principle of a pattern and present them from a C++ point of view. If you want to have more details, there is excellent documentation available. Here are a few choices:

Creational patterns deal with object creation. Let’s dive deeper.

Creational Patterns

I will write about two of the five creational patterns: Factory Method and Singleton. I know, I know the Singleton could also be regarded as an Anti-Pattern. In a later post, I will discuss the Singleton in more depth. Let’s start with the Factory Method:

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

Be part of my mentoring programs:

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (starts March 2024)
  • Do you want to stay informed: Subscribe.

     

    Factory Method

    Here are the facts:

    Purpose

    The factory method defines an interface to create a single object but lets subclasses decide which objects to create. The interface can provide a default implementation for creating objects.

    Also known as

    Virtual constructor

    Use Case

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

    Example

    Each container of the Standard Template Library has eight factory functions to generate various iterators.

    • begin, cbegin: returns an iterator to the beginning of the container
    • end, cend: returns an iterator to the end of the container
    • rbegin, crbegin: returns a reverse iterator to the beginning of the container
    • rend, crend: returns a reverse iterator to the end of the container
    The factory functions starting with c, return constant iterators.

    Structure

    FactoryMethod

     

    Product

    • Objects created by factoryMethod.

    Concrete Product

    • Implements the interface

    Creator

    • Declares the factory method
    • Calls the factory method

    Concrete Creator

    • Overwrites the factory method

    The Creator does not instantiate the Concrete Product. It calls its virtual member function factoryMethod. Consequentially, the Concrete Product is created by the Concrete Creator, and the object creation is independent of the Creator.

    This pattern is also known as a virtual constructor.

    Related Patterns

    Virtual Constructor

    Honestly, the name virtual constructor is misleading. We don’t have a virtual constructor in C++, but we have virtual construction to simulate a virtual constructor.

    Assume you have a class hierarchy with an interface class 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 to create a new Window based on an existing one. This means that when you put an instance of DefaultWindow or FancyWindow inside the factory function getNewWindow, it should return an instance of the same class.

    Classically, the factory method is implemented using an enum and a factory function. Here is the first try:

    // factoryMethodClassic.cpp
    
    #include <iostream>
    
    enum class WindowType {                                          // (5)
        DefaultWindow,
        FancyWindow
    };
    
    // Product
    class Window { 
     public: 
        virtual ~Window() {};
        virtual WindowType getType() const = 0;
        virtual std::string getName() const = 0;
    };
    
    // Concrete Products 
    class DefaultWindow: public Window { 
     public:
        WindowType getType() const override {
            return WindowType::DefaultWindow;
        }
        std::string getName() const override { 
            return "DefaultWindow";
        }
    };
    
    class FancyWindow: public Window {
     public: 
         WindowType getType() const override {
            return WindowType::FancyWindow;
        }
        std::string getName() const override { 
            return "FancyWindow";
        }
    };
    
    // Concrete Creator or Client
    Window* getNewWindow(Window* window) {                           // (1)
        switch(window->getType()){                                   // (4)
        case WindowType::DefaultWindow:
            return new DefaultWindow();
            break;
        case WindowType::FancyWindow:
            return new FancyWindow();
            break;
        }
        return nullptr;
    }
      
    int main() {
    
        std::cout << '\n';
    
        DefaultWindow defaultWindow;
        FancyWindow fancyWindow;
    
        const Window* defaultWindow1 = getNewWindow(&defaultWindow); // (2)
        const Window* fancyWindow1 = getNewWindow(&fancyWindow);     // (3)
    
        std::cout << defaultWindow1->getName() << '\n';
        std::cout << fancyWindow1->getName() << '\n';
      
        delete defaultWindow1;
        delete fancyWindow1;
    
        std::cout << '\n';
      
    }
    

     

    The factory function in line (1) decides, based on the incoming Window, which Window (lines 2 and 3) should be created. It uses window->getType() (line 4) to get the right Window type. The WindowType is an enumeration.

    Finally, here is the output of the program:

    factoryMethodClassic

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

    1. When my application should support, I would have to extend the enumeration WindowType and the switch statement.
    2. The switch statement becomes more and more difficult to maintain if I add new WindowType‘s.
    3. The code is too complicated. This is mainly due to the switch statement.

    Let me replace the switch statement with a virtual dispatch. Additionally, I also want to clone the existing Window‘s.

    // factoryMethod.cpp
    
    #include <iostream>
    
    // 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';
      
    }
    

     

    Window supports now two ways to create new ones: a default constructed Window with the member function create (line 1) and a copy constructed Window with the member function clone (line 2). The subtle difference is that the constructor takes the this pointer in the member function clone. (line The factory functions createWindow (line 3) and cloneWindow (line 4) dispatch on the dynamic type.

    The output of the program is promising. Both member functions create and clone display the name of the object they create.

    factoryMethodPointer 

    By the way. It is fine that the virtual member functions create and clone member functions of the DefaultWindow and the FancyWindow are private, because you use them via the Window interface. In the interface, both member function are public.

    What’s Next?

    Am I’m done with the factory method? NO! The program factoryMethod.cpp has two serious issues. ownership semantic and slicing. Let me write more about it in my next post.

     

     

     

     

     

     

    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, moon, Philipp Lenk, Hobsbawm, Charles-Jianye Chen, and Keith Jeffery.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Seminars

    I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

    Standard Seminars (English/German)

    Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

    • C++ – The Core Language
    • C++ – The Standard Library
    • C++ – Compact
    • C++11 and C++14
    • Concurrency with Modern C++
    • Design Pattern and Architectural Pattern with C++
    • Embedded Programming with Modern C++
    • Generic Programming (Templates) with C++
    • Clean Code with Modern C++
    • C++20

    Online Seminars (German)

    Contact Me

    Modernes C++ Mentoring,

     

     

    0 replies

    Leave a Reply

    Want to join the discussion?
    Feel free to contribute!

    Leave a Reply

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