Introduction
The Factory Method is a creational design pattern that provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created. This pattern promotes loose coupling by eliminating the need to bind application-specific classes into the code.
Key Concepts
- Factory Method: A method that returns an instance of a class.
- Product: The object that is being created.
- Creator: The class that contains the factory method.
- Concrete Product: The actual instance of the product that is created by the factory method.
Structure
The Factory Method pattern involves the following components:
- Product: Defines the interface of objects the factory method creates.
- ConcreteProduct: Implements the Product interface.
- Creator: Declares the factory method, which returns an object of type Product.
- ConcreteCreator: Overrides the factory method to return an instance of ConcreteProduct.
UML Diagram
+----------------+ +----------------+ | Creator | | Product | +----------------+ +----------------+ | + factoryMethod() : Product |<|--+ ConcreteProduct +----------------+ +----------------+ ^ ^ | | +----------------+ +----------------+ | ConcreteCreator | | ConcreteProduct | +----------------+ +----------------+ | + factoryMethod() : Product | +----------------+
Example
Let's consider a scenario where we need to create different types of documents (e.g., Word, PDF). We can use the Factory Method pattern to achieve this.
Step-by-Step Implementation
- Define the Product Interface:
- Create Concrete Products:
class WordDocument(Document): def open(self): return "Opening Word document" class PDFDocument(Document): def open(self): return "Opening PDF document"
- Define the Creator Class:
class Application(ABC): @abstractmethod def create_document(self) -> Document: pass def open_document(self): doc = self.create_document() return doc.open()
- Create Concrete Creators:
class WordApplication(Application): def create_document(self) -> Document: return WordDocument() class PDFApplication(Application): def create_document(self) -> Document: return PDFDocument()
- Client Code:
def client_code(app: Application): print(app.open_document()) if __name__ == "__main__": word_app = WordApplication() client_code(word_app) pdf_app = PDFApplication() client_code(pdf_app)
Explanation
- Document: The abstract base class for all documents.
- WordDocument and PDFDocument: Concrete implementations of the Document interface.
- Application: The abstract base class that declares the factory method
create_document
. - WordApplication and PDFApplication: Concrete implementations of the Application class that override the
create_document
method to return instances of WordDocument and PDFDocument, respectively.
Practical Exercise
Exercise
Create a factory method pattern to handle the creation of different types of notifications (e.g., Email, SMS).
- Define an abstract
Notification
class with a methodsend
. - Create concrete classes
EmailNotification
andSMSNotification
that implement theNotification
interface. - Define an abstract
NotificationFactory
class with a methodcreate_notification
. - Create concrete classes
EmailNotificationFactory
andSMSNotificationFactory
that override thecreate_notification
method. - Write client code to demonstrate the usage of the factory method pattern.
Solution
- Define the Notification Interface:
- Create Concrete Notifications:
class EmailNotification(Notification): def send(self): return "Sending Email Notification" class SMSNotification(Notification): def send(self): return "Sending SMS Notification"
- Define the NotificationFactory Class:
class NotificationFactory(ABC): @abstractmethod def create_notification(self) -> Notification: pass def notify(self): notification = self.create_notification() return notification.send()
- Create Concrete Factories:
class EmailNotificationFactory(NotificationFactory): def create_notification(self) -> Notification: return EmailNotification() class SMSNotificationFactory(NotificationFactory): def create_notification(self) -> Notification: return SMSNotification()
- Client Code:
def client_code(factory: NotificationFactory): print(factory.notify()) if __name__ == "__main__": email_factory = EmailNotificationFactory() client_code(email_factory) sms_factory = SMSNotificationFactory() client_code(sms_factory)
Common Mistakes and Tips
-
Mistake: Forgetting to override the factory method in the concrete creator classes.
- Tip: Always ensure that the concrete creator classes provide an implementation for the factory method.
-
Mistake: Creating instances of concrete products directly in the client code.
- Tip: Use the factory method to create instances to maintain loose coupling.
Conclusion
The Factory Method pattern is a powerful tool for creating objects without specifying the exact class of object that will be created. By using this pattern, you can achieve greater flexibility and maintainability in your code. In the next section, we will explore another creational pattern: the Abstract Factory.
Software Design Patterns Course
Module 1: Introduction to Design Patterns
- What are Design Patterns?
- History and Origin of Design Patterns
- Classification of Design Patterns
- Advantages and Disadvantages of Using Design Patterns
Module 2: Creational Patterns
Module 3: Structural Patterns
Module 4: Behavioral Patterns
- Introduction to Behavioral Patterns
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
Module 5: Application of Design Patterns
- How to Select the Right Pattern
- Practical Examples of Pattern Usage
- Design Patterns in Real Projects
- Refactoring Using Design Patterns
Module 6: Advanced Design Patterns
- Design Patterns in Modern Architectures
- Design Patterns in Microservices
- Design Patterns in Distributed Systems
- Design Patterns in Agile Development