Factory & Abstract Factory
Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 92 of 104.
The Factory pattern delegates object creation to a separate method or class, letting you create objects without specifying their exact class. This is useful when the creation logic is complex or when you want to decouple client code from concrete implementations.
A simple factory uses a method that returns different derived types based on input:
#include <iostream>
#include <memory>
class Button {
public:
virtual void render() = 0;
virtual ~Button() = default;
};
class WindowsButton : public Button {
public:
void render() override { std::cout << "Windows button\n"; }
};
class MacButton : public Button {
public:
void render() override { std::cout << "Mac button\n"; }
};
// Factory function
std::unique_ptr<Button> createButton(const std::string& os) {
if (os == "windows") return std::make_unique<WindowsButton>();
if (os == "mac") return std::make_unique<MacButton>();
return nullptr;
}The Abstract Factory takes this further by creating families of related objects. Instead of one factory method, you have a factory interface with multiple creation methods:
class Checkbox {
public:
virtual void check() = 0;
virtual ~Checkbox() = default;
};
class WindowsCheckbox : public Checkbox {
public:
void check() override { std::cout << "Windows checkbox\n"; }
};
class MacCheckbox : public Checkbox {
public:
void check() override { std::cout << "Mac checkbox\n"; }
};
// Abstract Factory
class GUIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
virtual ~GUIFactory() = default;
};
class WindowsFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<Checkbox> createCheckbox() override {
return std::make_unique<WindowsCheckbox>();
}
};Use Factory when you need flexible object creation, and Abstract Factory when you need to ensure that related objects work together consistently.
Challenge
EasyLet's build a Document Generator system using the Factory and Abstract Factory patterns. You'll create a system that can generate different types of documents (reports and invoices) for different output formats (PDF and HTML), demonstrating how factories decouple object creation from client code.
You'll organize your code across four files:
Document.h: Define your document product hierarchy.Create an abstract base class
Documentwith a pure virtual methodgenerate()that returns a string describing the generated document, and a virtual destructor.Then create four concrete document classes:
PDFReport—generate()returns"Generating PDF Report"HTMLReport—generate()returns"Generating HTML Report"PDFInvoice—generate()returns"Generating PDF Invoice"HTMLInvoice—generate()returns"Generating HTML Invoice"
DocumentFactory.h: Implement both a simple factory function and an abstract factory.First, create a simple factory function called
createDocumentthat takes two strings: a document type ("report"or"invoice") and a format ("pdf"or"html"). It should return astd::unique_ptr<Document>pointing to the appropriate concrete document. Returnnullptrfor invalid combinations.Then create an abstract factory class called
DocumentFactorywith two pure virtual methods:createReport()— returns astd::unique_ptr<Document>createInvoice()— returns astd::unique_ptr<Document>
Implement two concrete factories:
PDFFactory— createsPDFReportandPDFInvoiceHTMLFactory— createsHTMLReportandHTMLInvoice
DocumentService.h: Create a service that uses the abstract factory.Define a
DocumentServiceclass that takes astd::unique_ptr<DocumentFactory>in its constructor. It should have a method calledgenerateAllDocuments()that uses the factory to create both a report and an invoice, then prints the result of callinggenerate()on each, one per line.main.cpp: Demonstrate both factory approaches.Read two inputs:
- A document type (
reportorinvoice) - A format (
pdforhtml)
First, use the simple factory function to create a document based on the inputs. Print the result of calling
generate()on it.Then print a blank line.
Next, create a
DocumentServicewith the appropriate factory based on the format input (usePDFFactoryforpdf,HTMLFactoryforhtml). CallgenerateAllDocuments()to show how the abstract factory creates a family of related documents.- A document type (
For example, with inputs report and pdf:
Generating PDF Report
Generating PDF Report
Generating PDF InvoiceWith inputs invoice and html:
Generating HTML Invoice
Generating HTML Report
Generating HTML InvoiceNotice how the simple factory creates a single document based on both parameters, while the abstract factory ensures that all documents created by a service share the same format—this is the key benefit of Abstract Factory when you need families of related objects that work together consistently.
Cheat sheet
The Factory pattern delegates object creation to a separate method or class, allowing you to create objects without specifying their exact class. This decouples client code from concrete implementations.
A simple factory uses a function that returns different derived types based on input:
class Button {
public:
virtual void render() = 0;
virtual ~Button() = default;
};
class WindowsButton : public Button {
public:
void render() override { std::cout << "Windows button\n"; }
};
class MacButton : public Button {
public:
void render() override { std::cout << "Mac button\n"; }
};
// Factory function
std::unique_ptr<Button> createButton(const std::string& os) {
if (os == "windows") return std::make_unique<WindowsButton>();
if (os == "mac") return std::make_unique<MacButton>();
return nullptr;
}The Abstract Factory pattern creates families of related objects. Instead of one factory method, you have a factory interface with multiple creation methods:
class Checkbox {
public:
virtual void check() = 0;
virtual ~Checkbox() = default;
};
class WindowsCheckbox : public Checkbox {
public:
void check() override { std::cout << "Windows checkbox\n"; }
};
class MacCheckbox : public Checkbox {
public:
void check() override { std::cout << "Mac checkbox\n"; }
};
// Abstract Factory
class GUIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
virtual ~GUIFactory() = default;
};
class WindowsFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<Checkbox> createCheckbox() override {
return std::make_unique<WindowsCheckbox>();
}
};Use Factory when you need flexible object creation. Use Abstract Factory when you need to ensure that related objects work together consistently as a family.
Try it yourself
#include <iostream>
#include <string>
#include <memory>
#include "Document.h"
#include "DocumentFactory.h"
#include "DocumentService.h"
using namespace std;
int main() {
// Read inputs
string documentType;
string format;
cin >> documentType;
cin >> format;
// TODO: Use the simple factory function to create a document
// based on documentType and format inputs
// Print the result of calling generate() on it
// Print a blank line
cout << endl;
// TODO: Create a DocumentService with the appropriate factory
// based on the format input (PDFFactory for "pdf", HTMLFactory for "html")
// Call generateAllDocuments() to demonstrate the abstract factory
return 0;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy10STL Overview
STL Overview & PhilosophySTL ContainersIteratorsSTL AlgorithmsFunctors & Lambda ExpressionsRecap - Word Frequency13Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory & Abstract FactoryBuilder PatternObserver PatternStrategy Pattern2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic Container