Introduction to the Factory Method Pattern

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. You’ll learn how to implement your own factory methods to instantiate different types of objects and see how this pattern allows your code to handle new object types with ease.

Understanding the Factory Method Pattern

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. This pattern promotes loose coupling by eliminating the need to specify the exact class of the object that will be created. Instead, the instantiation is handled by subclasses.

You should consider using the Factory Method Pattern when object creation requires conditional logic, when working with large class hierarchies, or when developing frameworks and libraries that need to allow users to extend and customize object creation.

To understand how the Factory Method Pattern is implemented, let's break the process down into intermediate steps.

Step 1: Define an Abstract Base Class

First, define an abstract base class Document with an abstract method Open. This class will serve as the template for different types of documents.

// Abstract class representing a Document
abstract class Document
{
    // Abstract method to be implemented by derived classes
    public abstract void Open();
}
Step 2: Create Concrete Subclasses

Next, create concrete subclasses of Document. Each subclass will implement the Open method. For example, let's define WordDocument and ExcelDocument.

// WordDocument class inheriting from Document
class WordDocument : Document
{
    // Implementation of the Open method for Word documents
    public override void Open() => Console.WriteLine("Opening Word document.");
}

// ExcelDocument class inheriting from Document
class ExcelDocument : Document
{
    // Implementation of the Open method for Excel documents
    public override void Open() => Console.WriteLine("Opening Excel document.");
}
Step 3: Define an Abstract Creator Class

Now, define an abstract base class DocumentCreator with an abstract method CreateDocument. This class will provide the interface for creating documents.

// Abstract class representing a Document Creator
abstract class DocumentCreator
{
    // Abstract method to be implemented by derived classes
    public abstract Document CreateDocument();
}
Step 4: Create Concrete Creator Subclasses

Create concrete subclasses of DocumentCreator. Each subclass will implement the CreateDocument method to instantiate and return a specific type of document.

// WordDocumentCreator class inheriting from DocumentCreator
class WordDocumentCreator : DocumentCreator
{
    // Implementation of the CreateDocument method to create a Word document
    public override Document CreateDocument() => new WordDocument();
}

// ExcelDocumentCreator class inheriting from DocumentCreator
class ExcelDocumentCreator : DocumentCreator
{
    // Implementation of the CreateDocument method to create an Excel document
    public override Document CreateDocument() => new ExcelDocument();
}
Step 5: Use the Factory Method Pattern

Finally, use the factory methods to create document objects without specifying their concrete classes.

class Program
{
    static void Main()
    {
        // Create a Word document using WordDocumentCreator
        DocumentCreator creator = new WordDocumentCreator();
        Document doc = creator.CreateDocument();
        doc.Open();  // Output: Opening Word document.

        // Create an Excel document using ExcelDocumentCreator
        creator = new ExcelDocumentCreator();
        doc = creator.CreateDocument();
        doc.Open();  // Output: Opening Excel document.
    }
}
Flexibility and Extensibility

One of the key advantages of the Factory Method Pattern is its flexibility and extensibility. For instance, adding a new type of document, such as a PdfDocument, does not require changes to the existing DocumentCreator or Document classes. Instead, you can create a new subclass of Document and a corresponding subclass of DocumentCreator.

// PdfDocument class inheriting from Document
class PdfDocument : Document
{
    // Implementation of the Open method for PDF documents
    public override void Open() => Console.WriteLine("Opening PDF document.");
}

// PdfDocumentCreator class inheriting from DocumentCreator
class PdfDocumentCreator : DocumentCreator
{
    // Implementation of the CreateDocument method to create a PDF document
    public override Document CreateDocument() => new PdfDocument();
}

This extensibility makes the pattern ideal for applications where new types of objects are frequently added. You can introduce new products without altering the existing structure, enhancing the maintainability and evolvability of your codebase.

Conclusion

The Factory Method Pattern is essential because it promotes flexibility and scalability in your code designs. By delegating the creation of objects to factory methods, you can easily introduce new types of objects without changing the existing code, leading to better maintainability. Whether you're developing software libraries, frameworks, or complex applications, this pattern helps you manage and scale object creation efficiently. Ready to enhance your coding skills? Let's delve into the Factory Method Pattern and see how you can apply it to write cleaner, more maintainable code. Let's start!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal