Lesson 7 | Factory Method: consequences |
Objective | Write class using Factory Method pattern. |
Factory Method Consequences
Factory Method Design Pattern Consequences
Write a class that uses the Factory Method pattern. Factory Method patterns take the last step toward removing all knowledge of the details of the subclass from the client classes. Indeed, in the Java class library, the concrete subclasses are often hidden inside the sun packages and are deliberately left undocumented. Of course this is not always what you want in a subclass, but when it is, the Factory Method gives it to you. The Factory Method pattern also makes programs dynamically extensible at runtime. New concrete subclasses can be added to the system without recompiling the existing code. Factory Methods are also useful in programs that require parallel class hierarchies. A parallel class hierarchy is one in which every Product
object requires one or more additional objects. For example, imagine that every time a
Vehicle
is created, a Driver
object also has to be created, a BusDriver
for a bus, a CarDriver
for a car, and so on. A Factory Method can create the parallel objects at the same time it creates the
product, guaranteeing that there's exactly one driver for each vehicle.
One disadvantage of the Factory Method pattern is that it can expand the total number of classes in a system. Every concrete Product
class also requires a concrete Creator
class. The parameterized Factory Method avoids this downside.
Factory Method Pattern in C++
The Factory Method Pattern is a Creational Design Pattern that provides an interface for creating an object but allows subclasses to alter the type of objects that will be created. It is a hallmark approach for promoting loose coupling between a client and a class that the client instantiates. Below is an example implemented in C++ that employs the Factory Method Pattern.
#include <iostream>
#include <memory>
// Product Interface
class Logger {
public:
virtual void log(const std::string& message) = 0;
};
// ConcreteProduct A
class ConsoleLogger : public Logger {
public:
void log(const std::string& message) override {
std::cout << "Console Logger: " << message << std::endl;
}
};
// ConcreteProduct B
class FileLogger : public Logger {
public:
void log(const std::string& message) override {
// Code to log message to a file
std::cout << "File Logger: " << message << std::endl;
}
};
// Creator Interface
class LoggerFactory {
public:
virtual std::unique_ptr<Logger> createLogger() = 0;
};
// ConcreteCreator A
class ConsoleLoggerFactory : public LoggerFactory {
public:
std::unique_ptr<Logger> createLogger() override {
return std::make_unique<ConsoleLogger>();
}
};
// ConcreteCreator B
class FileLoggerFactory : public LoggerFactory {
public:
std::unique_ptr<Logger> createLogger() override {
return std::make_unique<FileLogger>();
}
};
int main() {
std::unique_ptr<LoggerFactory> loggerFactory = std::make_unique<ConsoleLoggerFactory>();
std::unique_ptr<Logger> logger = loggerFactory->createLogger();
logger->log("Test message");
return 0;
}
Components Explained
- Product Interface (`Logger` Class): This is the product interface that declares the type of object the factory method will create. It may also contain default implementation, but in this example, it's an abstract class with a pure virtual function.
class Logger {
public:
virtual void log(const std::string& message) = 0;
};
- Concrete Products (`ConsoleLogger` and `FileLogger` Classes)
These are the classes that implement the `Logger` interface. They are the objects that the Factory Method will create and return.
class ConsoleLogger : public Logger {...}
class FileLogger : public Logger {...}
- Creator Interface (`LoggerFactory` Class)
This is an interface declaring the Factory Method, which returns an object of the `Logger` type. The method is declared as virtual to allow subclasses to provide their implementations.
class LoggerFactory {
public:
virtual std::unique_ptr<Logger> createLogger() = 0;
};
- Concrete Creators (`ConsoleLoggerFactory` and `FileLoggerFactory` Classes)
These classes implement the `LoggerFactory` interface and override the Factory Method to return instances of `ConsoleLogger` and `FileLogger`, respectively.
class ConsoleLoggerFactory : public LoggerFactory {...}
class FileLoggerFactory : public LoggerFactory {...}
Summary
- The Product Interface (`Logger`) provides a uniform type for all the objects the factory method creates.
- The Concrete Products (`ConsoleLogger`, `FileLogger`) are the classes that implement the `Logger` interface.
- The Creator Interface (`LoggerFactory`) contains the declaration for the Factory Method.
- The Concrete Creators (`ConsoleLoggerFactory`, `FileLoggerFactory`) implement the Factory Method and are responsible for creating and returning the appropriate `Logger` subclass.
By adhering to the Factory Method Pattern, the example satisfies the principles of encapsulation and abstraction, facilitating a decoupling of client code from the specific classes being instantiated. This establishes an environment conducive for system adaptability and extensibility, crucial attributes for robust software architecture.
Factory Consequences - Exercise
Ad Microservices Patterns