Software development: The design pattern factory method for creating objects

0
11
software development the design pattern factory method for creating objects.jpg
software development the design pattern factory method for creating objects.jpg

The factory method from the book “Design Patterns” is also known as the virtual constructor. It defines an interface to create an object.

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.

 

Modern C++ – Rainer Grimm

 


Rainer Grimm has been working 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 likes to speak frequently 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 that mean first of all?

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

Before I start with the generation patterns, I would like 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 presented design patterns is intentionally 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:

  • The classic: “Design Patterns: Elements of Reusable Object-Oriented Software”
  • A good introduction: “Head First Design Patterns”
  • Wikipedia article: Design Patterns

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 to create 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 doesn’t know what kind of objects to create
  • Subclasses decide 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 that start with c return constant iterators.

 

product

  • Objects by factoryMethod to be created.

concrete product

  • Implements the interface

Creator

  • Declares the factory method
  • Calls the factory method

Concrete Creator

  • Overrides the factory method

The Creator does not instantiate the concrete product. He calls his virtual member function factoryMethod on. Consequently, the concrete product is created by the concrete creator, and 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 construction 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 that on top of an already existing one Window based. That is, if you have an instance of DefaultWindow or FancyWindow in the factory function 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) decides based on the incoming Window which Window (2 and 3) is to be created. she uses window->getType() (4) to get the right one WindowType to investigate. Of the WindowType is an enumeration.

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

  1. If my application new Windows to support, I would need the enumeration WindowType and the switch-Extend instruction.
  2. the switch-Instruction becomes more and more difficult to maintain when I add new ones WindowType add.
  3. The code is too complicated. This is mainly due to the switch-Instruction.

That’s why I will switch-Replace instruction with a virtual dispatch. I also want the existing ones Windowclone 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';
  
}

 

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

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

 

By the way: It’s okay that the virtual member functions create and clone of DefaultWindow and des FancyWindow are private since they are about the Windowinterface can be used. In the interface, both member functions are public.

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

SEE ALSO  WhatsApp continues to think about what to do with the states and tests a new design that gives them more prominence