Software development: The design pattern factory method for creating objects
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.
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.
Short 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:
- As a software developer, what patterns have I encountered most often over the past twenty years?
- 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.
generation pattern pattern
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.
factory method
Here are the facts:
purpose
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.
Also known as
Virtual Constructor
use case
- 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
example
Each Standard Template Library container has eight factory functions to create different iterators.
begin, cbegin
: returns an iterator pointing to the beginning of the containerend, cend
: returns an iterator pointing to the end of the containerbegin, crbegin
: returns a backward iterator pointing to the beginning of the containerrend, 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.
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:
- If my application new
Window
s to support, I would need the enumerationWindowType
and theswitch
-Extend instruction. - the
switch
-Instruction becomes more and more difficult to maintain when I add new onesWindowType
add. - 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 Window
clone 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 Window
s 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 Window
interface can be used. In the interface, both member functions are public.
What’s next?
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.