Welcome back! So far, you’ve learned about the Singleton Pattern and have seen how it ensures a class has only one instance with a global access point. Now, we’re moving on to another essential creational design pattern: the Factory Method Pattern. This pattern is all about creating objects in a much more flexible way than direct instantiation.
In this lesson, you'll explore the Factory Method Pattern in Ruby. We'll cover the following key points:
- Understanding the Factory Method Pattern: Learn about the core concepts and when to use this pattern.
- Implementing a Factory Method: Discover how to create your own factory methods to instantiate different types of objects in Ruby.
- Flexibility and Extensibility: See how the Factory Method Pattern allows your code to handle new object types with ease using Ruby’s dynamic nature.
Here's a glimpse of the code you’ll be working through:
document.rb
file that defines the interface for product
(Document) and concrete products (WordDocument
, ExcelDocument
):
Ruby1# Document class acting as an interface 2class Document 3 def open 4 raise NotImplementedError, "This method should be overridden." 5 end 6end 7 8# WordDocument implementation 9class WordDocument < Document 10 def open 11 puts 'Opening Word document.' 12 end 13end 14 15# ExcelDocument implementation 16class ExcelDocument < Document 17 def open 18 puts 'Opening Excel document.' 19 end 20end
document_creator.rb
file that defines the DocumentCreator
, responsible for creating all types of documents:
Ruby1# DocumentCreator class acting as a single factory 2class DocumentCreator 3 def create_document(type) 4 case type 5 when :word 6 WordDocument.new 7 when :excel 8 ExcelDocument.new 9 else 10 raise "Unsupported document type: #{type}" 11 end 12 end 13end
main.rb
file that demonstrates the usage of the Factory Method Pattern:
Ruby1# Main script demonstrating the Factory Method Pattern 2 3creator = DocumentCreator.new 4 5# Create a Word document using DocumentCreator and open it 6doc = creator.create_document(:word) 7doc.open 8 9# Create an Excel document using DocumentCreator and open it 10doc = creator.create_document(:excel) 11doc.open
This snippet demonstrates a simple implementation of the Factory Method Pattern using different document types. Let's break down the code and understand how the Factory Method Pattern works in practice:
- Document: A base class representing a document interface with a method
open
that raises an exception if not implemented. This class defines the common behavior for all document types. Note: In Ruby, theDocument
class acts as a "duck-typed interface." This means it defines a common method (open
) that all subclasses are expected to implement. While Ruby doesn’t enforce interfaces like Java or C#, raising aNotImplementedError
serves as a runtime safeguard to ensure the method is overridden in subclasses.- WordDocument and ExcelDocument: Concrete classes that implement the
Document
interface with specificopen
methods for Word and Excel documents, respectively.
- WordDocument and ExcelDocument: Concrete classes that implement the
- DocumentCreator: A single class responsible for creating any type of document. The
create_document
method instantiates the appropriate document object based on the input type parameter. TheDocumentCreator
class encapsulates the logic for deciding which type of document to create. By centralizing this decision-making, the class shields the rest of the application from needing to know the specifics of each document type. This decoupling means the client code focuses solely on using the returned objects rather than worrying about their creation.
The Factory Method Pattern is widely used in software development to create objects without specifying the exact class of the object that will be created. Here are some common scenarios where you can apply this pattern:
- Object Creation Flexibility: When you want to create objects without knowing the exact class type at runtime, the Factory Method Pattern provides a flexible way to instantiate objects based on runtime conditions.
- Object Initialization Logic: If you need to encapsulate complex object creation logic or initialization steps, the factory method can handle these tasks efficiently.
- Object Type Abstraction: When you want to decouple the client code from the concrete classes it uses, the factory method allows you to work with abstract interfaces instead of specific implementations.
Let's also understand the pros and cons of using the Factory Method Pattern:
- Pros:
- Flexibility: The Factory Method Pattern allows you to create objects dynamically based on runtime conditions, making your code more flexible and adaptable.
- Extensibility: You can easily add new types of objects without modifying existing code, promoting code scalability and maintainability.
- Encapsulation: The factory method encapsulates object creation logic, providing a clear separation between object creation and object usage.
- Cons:
- Complexity: Introducing multiple factory methods can lead to a complex class hierarchy, making the code harder to understand and maintain. If you have too many types of objects or factory methods, the
DocumentCreator
class could grow unwieldy, making it harder to maintain. For instance, if you need to support 10 different document types, thecase
statement could become bloated.
- Complexity: Introducing multiple factory methods can lead to a complex class hierarchy, making the code harder to understand and maintain. If you have too many types of objects or factory methods, the
The Factory Method Pattern is crucial because it promotes flexibility in your code designs. By delegating the creation of objects to factory methods, you can easily introduce new types of objects without changing existing code. This leads to better code maintainability and scalability. Whether you're developing software libraries, frameworks, or complex applications, this pattern helps you manage and scale object creation efficiently.