The Factory Method
This post is a cross-post from www.ModernesCpp.com.
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.
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.
- Which patterns did I encounter most often as a software developer in the last twenty years?
- 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:
- The classic: "Design Patterns: Elements of Reusable Object-Oriented Software"
- Nice introduction: "Head First Design Patterns"
- Wikipedia articles: Design Patterns
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:
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
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 virtual constructor.
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'; }
Based on the incoming Window, the factory function in line (1) decides 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:
Honestly, I don't like this solution for the following reasons:
- When my application should support, I would have to extend the enumeration WindowType and the switch statement.
- The switch statement becomes more and more difficult to maintain if I add new WindowType's.
- 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.
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.
Modernes C++ Mentoring
- "Fundamentals for C++ Professionals": Reopens October 2022
- "Design Patterns and Architectural Patterns in C++": Starts January 2023
Do you want to stay informed? Write to info@ModernesCpp.de.
Senior IT Consultant & Trainer | 30+ Years Leading IT Innovation
2yThe fact to instanciate initially two objects then clone from them new objects is in fact the solution proposed by the creational Gof paytern "ptototype". Prototype expects (as Open/close principle) a registry container holding all objects of concrete classes.. I see that solution proposed here was ended to this pattern !
Senior IT Consultant & Trainer | 30+ Years Leading IT Innovation
2yIs it the Gof Prototype (missing the regisstry) here ?