| The Factory Method design pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate. It defers instantiation to subclasses, allowing a class to delegate the instantiation logic to its subclasses, promoting loose coupling and making it easier to introduce new object types without modifying existing code.
The Factory Method design pattern is a creational pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created. It's a way to defer instantiation to subclasses. Instead of directly creating objects within a class, a factory method is defined, which subclasses can override to return different concrete object types. 
Here's a breakdown of the key components and benefits: Key Components: - Product: This is the interface or abstract class that defines the type of objects the factory method creates. Concrete products are the actual objects created.
- Concrete Product: These are the classes that implement the Product interface. Each concrete product represents a specific variation of the object.
- Creator: This is an abstract class or interface that declares the factory method. It may also implement a default factory method to provide a default product.
- Concrete Creator: These are the subclasses of the Creator that implement the factory method to return specific Concrete Product objects.
How it works: 1. A client needs an object of a certain type. 2. The client interacts with the Creator class (or its subclass). 3. The Creator's factory method is called. 4. The Concrete Creator subclass's implementation of the factory method determines which Concrete Product to instantiate and return. The client receives the product without needing to know the specifics of its creation. Example (Python): Let's say we have a factory for creating different types of documents: from abc import ABC, abstractmethod
# Abstract Product
class Document(ABC):
@abstractmethod
def open(self):
pass
# Concrete Products
class PDFDocument(Document):
def open(self):
return "Opening a PDF document."
class WordDocument(Document):
def open(self):
return "Opening a Word document."
class TextDocument(Document):
def open(self):
return "Opening a Text document."
# Abstract Creator
class DocumentFactory(ABC):
@abstractmethod
def create_document(self) -> Document:
pass
# Concrete Creators
class PDFDocumentFactory(DocumentFactory):
def create_document(self) -> Document:
return PDFDocument()
class WordDocumentFactory(DocumentFactory):
def create_document(self) -> Document:
return WordDocument()
class TextDocumentFactory(DocumentFactory):
def create_document(self) -> Document:
return TextDocument()
# Client Code
def open_document(factory: DocumentFactory):
document = factory.create_document()
print(document.open())
# Usage
if __name__ == "__main__":
pdf_factory = PDFDocumentFactory()
word_factory = WordDocumentFactory()
text_factory = TextDocumentFactory()
open_document(pdf_factory) # Output: Opening a PDF document.
open_document(word_factory) # Output: Opening a Word document.
open_document(text_factory) # Output: Opening a Text document.
C++ Example #include <iostream>
#include <memory>
#include <string>
// Abstract Product
class Document {
public:
virtual void open() const = 0; // Pure virtual function
virtual ~Document() = default;
};
// Concrete Products
class PDFDocument : public Document {
public:
void open() const override {
std::cout << "Opening a PDF document." << std::endl;
}
};
class WordDocument : public Document {
public:
void open() const override {
std::cout << "Opening a Word document." << std::endl;
}
};
class TextDocument : public Document {
public:
void open() const override {
std::cout << "Opening a Text document." << std::endl;
}
};
// Abstract Creator
class DocumentFactory {
public:
virtual std::unique_ptr<Document> createDocument() const = 0; // Factory Method
virtual ~DocumentFactory() = default;
};
// Concrete Creators
class PDFDocumentFactory : public DocumentFactory {
public:
std::unique_ptr<Document> createDocument() const override {
return std::make_unique<PDFDocument>();
}
};
class WordDocumentFactory : public DocumentFactory {
public:
std::unique_ptr<Document> createDocument() const override {
return std::make_unique<WordDocument>();
}
};
class TextDocumentFactory : public DocumentFactory {
public:
std::unique_ptr<Document> createDocument() const override {
return std::make_unique<TextDocument>();
}
};
// Client Code
void openDocument(const DocumentFactory& factory) {
std::unique_ptr<Document> document = factory.createDocument();
document->open();
}
// Main Function
int main() {
PDFDocumentFactory pdfFactory;
WordDocumentFactory wordFactory;
TextDocumentFactory textFactory;
openDocument(pdfFactory); // Output: Opening a PDF document.
openDocument(wordFactory); // Output: Opening a Word document.
openDocument(textFactory); // Output: Opening a Text document.
return 0;
}
Benefits: - Open/Closed Principle: You can introduce new types of products without modifying existing code. Simply create a new Concrete Creator and Concrete Product.
- Loose Coupling: The client is decoupled from the concrete product classes.
- Improved Code Organization: The instantiation logic is encapsulated within the factory, making the code cleaner and easier to maintain.
- Flexibility: Allows easy switching between different product types without changing the client code.
When to use it: - When you want to create families of related objects without specifying their concrete classes.
- When the creation logic should be encapsulated.
- When you need to decouple client code from the concrete products being created.
- When you anticipate the need to add new product types in the future.
The Factory Method pattern is a powerful tool for creating flexible and maintainable object-oriented code. It's especially useful when dealing with complex object creation scenarios where many different variations are possible. |