CreationalC++verifiedVerified
Factory Method Pattern in C++
Defines an interface for creating an object but lets subclasses decide which class to instantiate. Defers instantiation to subclasses.
How to Implement the Factory Method Pattern in C++
1Step 1: Define the Product interface
class Transport {
public:
virtual ~Transport() = default;
virtual std::string deliver() const = 0;
};2Step 2: Concrete products
class Truck : public Transport {
public:
std::string deliver() const override { return "Delivering by truck"; }
};
class Ship : public Transport {
public:
std::string deliver() const override { return "Delivering by ship"; }
};3Step 3: Define the Creator with the factory method
class Logistics {
public:
virtual ~Logistics() = default;
virtual std::unique_ptr<Transport> createTransport() const = 0;
std::string planDelivery() const {
auto transport = createTransport();
return transport->deliver();
}
};4Step 4: Concrete creators override the factory method
class RoadLogistics : public Logistics {
public:
std::unique_ptr<Transport> createTransport() const override {
return std::make_unique<Truck>();
}
};
class SeaLogistics : public Logistics {
public:
std::unique_ptr<Transport> createTransport() const override {
return std::make_unique<Ship>();
}
};5Step 5: Client code works with the Creator interface
int main() {
std::unique_ptr<Logistics> logistics = std::make_unique<RoadLogistics>();
std::cout << logistics->planDelivery() << "\n";
logistics = std::make_unique<SeaLogistics>();
std::cout << logistics->planDelivery() << "\n";
}#include <iostream>
#include <string>
#include <memory>
#include <map>
#include <functional>
#include <format>
#include <stdexcept>
#include <concepts>
// [step] Define the Product interface with concepts
class ILogger {
public:
virtual ~ILogger() = default;
virtual void log(const std::string& message) = 0;
virtual std::string name() const = 0;
};
template <typename T>
concept LoggerType = std::derived_from<T, ILogger>;
// [step] Concrete loggers
class ConsoleLogger : public ILogger {
public:
void log(const std::string& message) override {
std::cout << std::format("[CONSOLE] {}\n", message);
}
std::string name() const override { return "ConsoleLogger"; }
};
class FileLogger : public ILogger {
std::string filepath_;
public:
explicit FileLogger(std::string path) : filepath_(std::move(path)) {}
void log(const std::string& message) override {
// In production, write to file
std::cout << std::format("[FILE:{}] {}\n", filepath_, message);
}
std::string name() const override {
return std::format("FileLogger({})", filepath_);
}
};
class JsonLogger : public ILogger {
public:
void log(const std::string& message) override {
std::cout << std::format(R"({{"level":"info","msg":"{}"}})", message) << "\n";
}
std::string name() const override { return "JsonLogger"; }
};
// [step] Registry-based factory with self-registration
class LoggerFactory {
using Creator = std::function<std::unique_ptr<ILogger>(const std::string&)>;
std::map<std::string, Creator> registry_;
LoggerFactory() {
// Register default loggers
registerType("console", [](const std::string&) {
return std::make_unique<ConsoleLogger>();
});
registerType("file", [](const std::string& config) {
return std::make_unique<FileLogger>(config.empty() ? "app.log" : config);
});
registerType("json", [](const std::string&) {
return std::make_unique<JsonLogger>();
});
}
public:
static LoggerFactory& instance() {
static LoggerFactory factory;
return factory;
}
void registerType(const std::string& name, Creator creator) {
registry_[name] = std::move(creator);
}
std::unique_ptr<ILogger> create(const std::string& type,
const std::string& config = "") const {
auto it = registry_.find(type);
if (it == registry_.end())
throw std::runtime_error(
std::format("Unknown logger type: '{}'", type));
return it->second(config);
}
std::vector<std::string> availableTypes() const {
std::vector<std::string> types;
for (const auto& [name, _] : registry_) types.push_back(name);
return types;
}
};
// [step] Demonstrate the factory
int main() {
auto& factory = LoggerFactory::instance();
auto console = factory.create("console");
auto file = factory.create("file", "/var/log/app.log");
auto json = factory.create("json");
for (auto* logger : {console.get(), file.get(), json.get()}) {
logger->log(std::format("Hello from {}", logger->name()));
}
// Register a custom logger at runtime
factory.registerType("null", [](const std::string&) -> std::unique_ptr<ILogger> {
struct NullLogger : ILogger {
void log(const std::string&) override {}
std::string name() const override { return "NullLogger"; }
};
return std::make_unique<NullLogger>();
});
auto null = factory.create("null");
null->log("This goes nowhere");
std::cout << "Registered types: " << factory.availableTypes().size() << "\n";
}Factory Method Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Factory Method Pattern in the Real World
“Think of a logistics company that ships packages. The headquarters defines the shipping process but doesn’t decide the vehicle. Regional offices (subclasses) choose whether to use trucks, ships, or drones based on local conditions. The headquarters just says ‘get me a transport’ and the regional office delivers the right one.”