Navigation

Sunday, 15 December 2024

The Factory Design Pattern in C#: With Practical Example.

 The Factory Design Pattern is one of the most commonly used patterns in software development. It provides a way to create objects without exposing the instantiation logic to the client and often involves a factory method or class responsible for the creation. This approach enhances code maintainability, reduces redundancy, and promotes the principle of single responsibility.

In this blog, we’ll explore the factory design pattern with a practical C# example, where we implement a PersonFactory to generate instances of a Person class. Along the way, we’ll discuss its significance and best practices.

The Problem: Generating Unique Objects

Imagine you’re developing an application where you need to manage a list of people, and each Person object must have a unique identifier (Id) alongside a Name. Manually managing unique IDs across various parts of your application could lead to potential bugs and duplicated code. This is where the factory pattern shines.


The Example Code

Below is the implementation of the factory design pattern using a PersonFactory:

using System;

namespace Coding.Exercise
{
    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class PersonFactory
    {
        private int _nextId = 0;

        public Person CreatePerson(string name)
        {
            return new Person
            {
                Id = _nextId++, // Assigns a unique ID
                Name = name
            };
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            PersonFactory factory = new PersonFactory();

            // Creating new Person instances
            var person1 = factory.CreatePerson("Alice");
            var person2 = factory.CreatePerson("Bob");

            Console.WriteLine($"Person 1: Id={person1.Id}, Name={person1.Name}");
            Console.WriteLine($"Person 2: Id={person2.Id}, Name={person2.Name}");
        }
    }
}


How It Works

  1. Person Class:

    • This represents the object being created.
    • It has two properties: Id (unique identifier) and Name.
  2. PersonFactory Class:

    • Responsible for creating instances of Person.
    • Maintains an internal _nextId field to ensure each Person object receives a unique Id.
  3. Factory in Action:

    • The CreatePerson method takes a name as input, generates a Person object, assigns a unique Id, and returns the new object.
  4. Usage:

    • Instead of directly instantiating Person objects, the client code delegates this responsibility to the factory.


Benefits of Using a Factory

  1. Encapsulation:

    • The logic for assigning unique IDs is hidden within the PersonFactory. The client code doesn’t need to worry about managing it.
  2. Reusability:

    • The factory can be reused in multiple parts of the application, reducing duplication.
  3. Scalability:

    • You can extend the factory logic (e.g., logging, validation, or more complex initialization) without affecting the client code.
  4. Single Responsibility Principle:

    • The PersonFactory focuses solely on the creation of Person objects, keeping the Person class and client code simpler.

A Better Example: Abstracting for More Flexibility

While the above example is great for understanding the basics, let’s consider a scenario where different types of people (e.g., Employee or Customer) need to be created. Using the factory pattern in this way allows for better scalability.


using System;

namespace FactoryPatternExample
{
    public abstract class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class Employee : Person
    {
        public string JobTitle { get; set; }
    }

    public class Customer : Person
    {
        public string Email { get; set; }
    }

    public class PersonFactory
    {
        private int _nextId = 0;

        public Person CreateEmployee(string name, string jobTitle)
        {
            return new Employee
            {
                Id = _nextId++,
                Name = name,
                JobTitle = jobTitle
            };
        }

        public Person CreateCustomer(string name, string email)
        {
            return new Customer
            {
                Id = _nextId++,
                Name = name,
                Email = email
            };
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var factory = new PersonFactory();

            var employee = factory.CreateEmployee("Alice", "Developer");
            var customer = factory.CreateCustomer("Bob", "bob@example.com");

Console.WriteLine($"Employee: Id={employee.Id}, Name={employee.Name}, Job Title={(employee as Employee)?.JobTitle}");
Console.WriteLine($"Customer: Id={customer.Id}, Name={customer.Name}, Email={(customer as Customer)?.Email}");
        }
    }
}




Key Takeaways

  • The factory design pattern simplifies object creation and promotes maintainable, scalable code.
  • In scenarios where object creation involves complexity (e.g., unique IDs, multiple types,
or additional initialization logic), factories centralize and manage this complexity effectively.
  • This approach is particularly useful when dealing with dynamically evolving requirements.






No comments:

Post a Comment