-
Factory Design Pattern
-
The Factory Design Pattern falls under the Creational Design Patterns Category. The Factory Pattern is a design pattern used in object-oriented programming to create objects without specifying the exact class of object that will be created. The Factory Design Pattern is one of the most frequently used design patterns in real-time applications.
According to Gang of Four (GoF), a factory is an object used to create other objects, means a factory is a class with a method. That method creates and returns different objects based on the received input parameter.
In the Factory Design pattern, we create an object without exposing the Object Creation and Initialization logic to the client, and the client will refer to the newly created object using a common interface. It provides a way to delegate the instantiation logic to a factory class or method, allowing for more flexible and scalable code. Let's see the implementation with very basic example, then we will see the real world examples.
Implementation
Scenario
Let's say you are building a system that deals with different types of shapes (Circle, Rectangle, etc…). You want to use a factory to create these shape objects based on user input or other conditions.
Step 1 - Define an Interface for the Shape:
public interface IShape { void Draw(); }
public class Circle : IShape { public void Draw() { Console.WriteLine("Drawing a Circle"); } } public class Rectangle : IShape { public void Draw() { Console.WriteLine("Drawing a Rectangle"); } }
public class ShapeFactory { public IShape GetShape(string shapeType) { if (shapeType == null) { return null; } if (shapeType.Equals("CIRCLE")) { return new Circle(); } else if (shapeType.Equals("RECTANGLE")) { return new Rectangle(); } return null; } }
public class Program { public static void Main(string[] args) { ShapeFactory shapeFactory = new ShapeFactory(); // Get an object of Circle and call its Draw method. IShape shape1 = shapeFactory.GetShape("CIRCLE"); shape1.Draw(); // Get an object of Rectangle and call its Draw method. IShape shape2 = shapeFactory.GetShape("RECTANGLE"); shape2.Draw(); } }
Explanation
- IShape Interface: This interface declares the Draw method that all shapes must implement.
- Circle and Rectangle Classes: These are concrete implementations of the IShape interface.
- ShapeFactory Class: This class has a method GetShape that returns an instance of a shape based on the shapeType string.
- Program Class: In the Main method, you use the ShapeFactory to get instances of Circle and Rectangle and call their Draw methods.
Benefits
- Encapsulation: The ShapeFactory encapsulates the object creation logic and keeps it separate from the main program.
- Flexibility: Adding new shapes is easy. You just need to create a new class that implements IShape and update the ShapeFactory accordingly.
- Decoupling: The Program class doesn't need to know about the concrete classes of shapes, just the IShape interface.
This simple example demonstrates how the Factory Design Pattern can be used to manage object creation and make code more flexible and maintainable. Now let's see some real-world examples of how the Factory Pattern might be applied.
Examples : Using Factory Design Pattern in C#
- Document Creation
Consider a scenario where an application can generate different types of documents (PDF, Word, Excel etc….). You don't want to tightly couple the client code to specific document types.
// Document interface public interface IDocument { void Print(); } // Concrete Products public class PdfDocument : IDocument { public void Print() { Console.WriteLine("Printing PDF Document"); } } public class WordDocument : IDocument { public void Print() { Console.WriteLine("Printing Word Document"); } } // Factory interface public interface IDocumentFactory { IDocument CreateDocument(); } // Concrete Factories public class PdfDocumentFactory : IDocumentFactory { public IDocument CreateDocument() { return new PdfDocument(); } } public class WordDocumentFactory : IDocumentFactory { public IDocument CreateDocument() { return new WordDocument(); } } // Client code class Program { static void Main(string[] args) { IDocumentFactory factory = new PdfDocumentFactory(); IDocument document = factory.CreateDocument(); document.Print(); // Output: Printing PDF Document factory = new WordDocumentFactory(); document = factory.CreateDocument(); document.Print(); // Output: Printing Word Document } }
- Database Connection
When an application needs to connect to different types of databases ( SQL Server, MySQL, Oracle etc…), a factory pattern can manage the creation of database connection objects.
// Database Connection interface public interface IDatabaseConnection { void Connect(); } // Concrete Class public class SqlServerConnection : IDatabaseConnection { public void Connect() { Console.WriteLine("Connecting to SQL Server"); } } public class MySqlConnection : IDatabaseConnection { public void Connect() { Console.WriteLine("Connecting to MySql"); } } public class OracleConnection : IDatabaseConnection { public void Connect() { Console.WriteLine("Connecting to Oracle"); } } // Factory interface public interface IDatabaseConnectionFactory { IDatabaseConnection CreateConnection(); } // Concrete Factories public class SqlServerConnectionFactory : IDatabaseConnectionFactory { public IDatabaseConnection CreateConnection() { return new SqlServerConnection(); } } public class MySqlConnectionFactory : IDatabaseConnectionFactory { public IDatabaseConnection CreateConnection() { return new MySqlConnection(); } } public class OracleConnectionFactory : IDatabaseConnectionFactory { public IDatabaseConnection CreateConnection() { return new OracleConnection(); } } // Client code class Program { static void Main(string[] args) { IDatabaseConnectionFactory factory = new SqlServerConnectionFactory(); IDatabaseConnection connection = factory.CreateConnection(); connection.Connect(); // Output: Connecting to SQL Server factory = new MySqlConnectionFactory(); connection = factory.CreateConnection(); connection.Connect(); // Output: Connecting to MySQL factory = new OracleConnectionFactory(); connection = factory.CreateConnection(); connection.Connect(); // Output: Connecting to Oracle } }
When and Where to Use Factory Design Pattern
When to Use:
- Complex Object Creation: When the creation process of an object is complex or requires multiple steps.
- Decoupling: When you want to decouple the client code from the concrete implementations of the objects it needs.
- Scalability: When you anticipate that new types of products might be added, and you want to minimize changes to the existing code.
- UI Components: For creating different types of UI controls dynamically.
- Data Access Layers: When creating different types of data access objects or repositories.
- Dependency Injection: When integrating with dependency injection frameworks, which often use factories to create and inject dependencies.
Summary
In each of these examples, the
is used to handle the creation of objects in a way that allows for easy extension and maintenance. By delegating the creation process to a factory, you decouple the client code from the specifics of the classes it needs, making the system more flexible and easier to adapt to new requirements or changes.