Back to: Design Patterns in C# With Real-Time Examples
Factory Design Pattern in C# with Real-Time Example
In this article, I will discuss the Factory Design Pattern in C# with examples. The Factory Design Pattern is one of the most frequently used design patterns in real-time applications. The Factory Design Pattern in C# falls under the Creational Design Patterns Category. As part of this article, we will discuss the following pointers related to the Factory Design Pattern.
- What is the Factory Design Pattern?
- Understanding the Factory Design Pattern.
- Example without using the Factory Pattern in C#.
- Understanding the Problem of not using the Factory Design Pattern
- Implementing the Factory Design Pattern in C#?
- When to use Factory Design Pattern in Real-Time Application?
- Advantages and Disadvantages of Factory Design Pattern.
What is Factory Design Pattern in C#?
Let us first try to understand the definitions of the factory design pattern. If you do not understand the following definition, don’t worry; we will explain the same with real-time examples, and then you will understand the definition.
According to Gang of Four (GoF), the Factory Design Pattern states that A factory is an object used for creating other objects. In technical terms, we can say that a factory is a class with a method. That method will create and return different objects based on the received input parameter.
In simple words, if we have a superclass and n number of subclasses, and based on the data provided, if we have to create and return the object of one of the subclasses, then we need to use the Factory Design Pattern in C#.
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. The basic principle behind the Factory Design Pattern is that, at run time, we get an object of a similar type based on the parameter we pass. So, the client will get the appropriate object and consume the object without knowing how the object is created and initialized.
Real-Time Example to Understand Factory Design Pattern in C#
Let us understand the Factory Design Pattern with one Real-Time Example. We are going to develop an application for showing Credit Card Details.
Please have a look at the following diagram. As you can see, we have three credit card classes, i.e., MoneyBack, Titanium, and Platinum. These three classes are the subclasses of the CreditCard superclass or, you can say, super interface. The CreditCard superclass or super interface has three methods, i.e., GetCardType, GetCreditLimit, and GetAnnualCharge. The subclasses, i.e., MoneyBack, Titanium, and Platinum, have implemented the above three methods of the CreditCard.
We are required to ask the user to select the credit card. Once the user selects the credit card, we need to display the required information about that selected credit card. Let us first discuss achieving this without using the Factory Design Pattern in C#. Then, we will discuss the problems, and finally, we will create the same application using the Factory Design Pattern in C#.
Example Without using Factory Design Pattern in C#
Let us first see the example without using the Factory Design Pattern. It’s very simple and straightforward. Let us proceed and implement this step by step.
Step 1: Create the Abstract Product or Product Interface (CreditCard)
Here, we need to create either an interface or an abstract class that will expose the operations a credit card should have. So, create a class file named CreditCard.cs and copy and paste the following code. As you can see, we created the CreditCard interface with three methods per our requirements.
namespace FactoryDesignPattern { public interface CreditCard { string GetCardType(); int GetCreditLimit(); int GetAnnualCharge(); } }
We need to create three Product classes to implement the above interface.
Step 2: Creating Product Classes (MoneyBack, Titanium, and Platinum)
In our example, we have three Credit cards. So, we need to create three classes by implementing the CreditCard Interface and providing implementation to all three CreditCard methods. First, create a class file named MoneyBack.cs and copy and paste the following code.
namespace FactoryDesignPattern { class MoneyBack : CreditCard { public string GetCardType() { return "MoneyBack"; } public int GetCreditLimit() { return 15000; } public int GetAnnualCharge() { return 500; } } }
As you can see, this class implements the CreditCard interface and provides implementation to all three methods. Similarly, we must do the same for the other two credit card classes.
Titanium.cs:
Create a class file named Titanium.cs and copy and paste the following code.
namespace FactoryDesignPattern { public class Titanium : CreditCard { public string GetCardType() { return "Titanium Edge"; } public int GetCreditLimit() { return 25000; } public int GetAnnualCharge() { return 1500; } } }
Platinum.cs:
Create a class file named Platinum.cs, and copy and paste the following code.
namespace FactoryDesignPattern { public class Platinum : CreditCard { public string GetCardType() { return "Platinum Plus"; } public int GetCreditLimit() { return 35000; } public int GetAnnualCharge() { return 2000; } } }
So, we have created three Product classes that implement the CreditCard interface. Next, we must consume the Product classes inside the client code by creating and initializing the appropriate Product class object.
Step 3: Client Code (Main Method)
Client Code is nothing but the class from where we need to consume the product classes (MoneyBack, Titanium, and Platinum). And in our example, it will be the Main method of the Program class. We will ask the user to select the Credit Card Type in the client code. Based on the Selected Credit card, we will create an instance of any of the above three product implementation classes (MoneyBack, Titanium, and Platinum) and call the methods to show the credit card details. So, modify the Main method of the Program class as follows. The following example code is self-explained, so please go through the comment lines for a better understanding.
using System; namespace FactoryDesignPattern { class Program { static void Main(string[] args) { //Generally we will get the Card Type from UI. //Here we are hardcoded the card type string cardType = "MoneyBack"; CreditCard cardDetails = null; //Based of the CreditCard Type we are creating the //appropriate type instance using if else condition if (cardType == "MoneyBack") { cardDetails = new MoneyBack(); } else if (cardType == "Titanium") { cardDetails = new Titanium(); } else if (cardType == "Platinum") { cardDetails = new Platinum(); } if (cardDetails != null) { Console.WriteLine("CardType : " + cardDetails.GetCardType()); Console.WriteLine("CreditLimit : " + cardDetails.GetCreditLimit()); Console.WriteLine("AnnualCharge :" + cardDetails.GetAnnualCharge()); } else { Console.Write("Invalid Card Type"); } Console.ReadLine(); } } }
The above code implementation is very straightforward. Once we get the CardType value, we create the appropriate Credit Card instance using the IF-ELSE Condition. Then, we call the three methods to display the credit card information on the console window. So, when you run the application, you will get the output as expected, as shown below.
What is the Problem with the above Code Implementation?
The above code implementation introduces the following problems
- First, the Tight Coupling between the client class (Program) and Product Classes (MoneyBack, Titanium, and Platinum). So, when we make changes in one class, we must also make changes in the other classes.
- Secondly, suppose we add a new Credit Card. In that case, we also need to modify the client code, i.e., the main method of the Program class, by adding an extra IF-ELSE Condition, which not only overheads the development but also the testing process.
Let us see how to overcome the above problem by using the Factory Design Pattern in C#.
Factory Design Pattern Implementation in C#:
As per the definition of Factory Design Pattern, the Factory Design Pattern creates an object without exposing the object creation logic to the client, and the client refers to the newly created object using a common interface.
Please have a look at the following image. Our factory class is responsible for creating and returning the appropriate Product (i.e., MoneyBack, Titanium, and Platinum) object. As you can see, this class has one static method, i.e., GetCreditcard, and this method takes one input parameter and, based on the parameter value it will create one of the credit card (i.e., MoneyBack, Platinum, and Titanium) objects and store that object in the superclass (CrditCard) reference variable and finally return that superclass reference variable to the caller of this method i.e. to the client or you can say in our example it is the Main method of the Program class.
Now, the client needs to get the object through CreditCardFactory. For example, if the client wants to create an instance of a Platinum card, he/she needs to do something like the below. As you can see, he/she needs to pass the Credit card type to the GetCreditcard method of the CreditCardFactory class. Now, the GetCreditcard() method will create a Platinum class instance and return that instance to the client.
Step 4: Creating Factory Class
Create a class file named CreditCardfactory.cs and copy and paste the following. This class contains the logic to create and initialize the appropriate object and returns that object based on some condition. As you can see, this class contains one static method. That static method takes one string parameter, and based on the parameter value, it will create and return the appropriate product instance (MoneyBack, Titanium, and Platinum) to the client.
namespace FactoryDesignPattern { public class CreditCardFactory { public static CreditCard GetCreditCard(string cardType) { CreditCard cardDetails = null; if (cardType == "MoneyBack") { cardDetails = new MoneyBack(); } else if (cardType == "Titanium") { cardDetails = new Titanium(); } else if (cardType == "Platinum") { cardDetails = new Platinum(); } return cardDetails; } } }
Step 5: Using Factory Class in Client Code to Get the Product Instance
Now, we need to modify the client code. So, modify the Main method of the Program class as follows. As you can see, now we are not creating the Product instance using IF-ELSE Condition; rather, we are calling the Static GetCreditCard method of the CreditCardFactory class by passing the Credit Card Type which instance we need to create. Once the GetCreditCard method returns the appropriate product instance, we consume the methods as it is. Here, we are storing the object in the super interface GetCreditCard.
using System; namespace FactoryDesignPattern { class Program { static void Main(string[] args) { CreditCard cardDetails = CreditCardFactory.GetCreditCard("Platinum"); if (cardDetails != null) { Console.WriteLine("CardType : " + cardDetails.GetCardType()); Console.WriteLine("CreditLimit : " + cardDetails.GetCreditLimit()); Console.WriteLine("AnnualCharge :" + cardDetails.GetAnnualCharge()); } else { Console.Write("Invalid Card Type"); } Console.ReadLine(); } } }
Output:
Factory Design Pattern UML (Class) Diagram:
So, once we understand how to implement the Factory Design Pattern in C#, let us try to understand the UML (Unified Modeling Language) or Class diagram of the Factory Design Pattern. The Unified Modeling Language is a general-purpose, developmental modeling language in software engineering that is intended to provide a standard way to visualize the design of a system. For a better understanding, please look at the following diagram, which shows the different components of the Factory Design Pattern. Here, I am comparing the Factory Design Pattern UML diagram with our CreditCard example so you can easily understand the concept.
Real-Life Example of Factory Pattern:
From Lehman’s point of view, we can say that a factory is a place where products are created. In other words, we can say that it is a centralized place for creating products. Later, based on the order received, the factory delivers the appropriate product. For example, a car factory can produce different types of cars. If you order a car based on your requirements or specifications, the factory will create the appropriate car and then deliver that car to you.
The same thing also happens in the Factory Design Pattern. A factory (i.e., a class) will create and deliver products (i.e., objects) based on the incoming parameters.
When to use the Factory Design Pattern in Real-Time Applications?
It would not be a good programming approach to specify the exact class name while creating the objects by the client, leading to tight coupling between the client and the product. To overcome this problem, we need to use the Factory Design Pattern in C#. This design pattern gives the client a simple mechanism to create the object. So, we need to use the Factory Design Pattern in C# when
- The Object needs to be extended to the subclasses
- Classes don’t know what exact sub-classes it has to create
- The Product implementation is going to change over time, and the Client remains unchanged
Problems of Simple Factory Pattern in C#
- If we need to add any new product (i.e., a new credit card), then we need to add a new if else condition in the GetCreditCard method of the CreditCardFactory class. This violates the open/closed design principle.
- We also have a tight coupling between the Factory (CreditCardFactory) and Product classes (MoneyBack, Titanium, and Platinum). We can overcome these problems using the Factory Method Design Pattern, which we will discuss in our upcoming articles.
Real-Time Applications of Factory Design Pattern
The Factory Design Pattern is widely used across various domains in software development due to its ability to decouple client code from specific classes and offer flexibility in object creation. Here are some real-time applications or scenarios where the Factory Design Pattern can be applied:
- GUI Libraries: Many GUI libraries, like those for desktop or mobile platforms, use factories to create UI elements. For instance, a ButtonFactory might produce buttons appropriate for Windows, macOS, or Linux, depending on the current operating system.
- Database Connections: A DatabaseConnectionFactory can create connections to different databases based on configuration or runtime requirements, e.g., connecting to SQL Server, MySQL, or Oracle.
- Payment Gateways: In e-commerce systems, a factory can instantiate appropriate payment gateway classes based on user selection or region. For example, choosing between PayPal, Stripe, or a bank-specific gateway.
- Reporting Tools: In applications where different report formats are supported, a ReportFactory can be used to produce the appropriate report format class, such as PDF, CSV, or Excel.
- Middleware or Plugin Systems: In systems that support plugins or middleware, a factory can determine which specific implementations to load based on configuration or system requirements.
- API Clients: In applications integrating with various APIs, a factory can instantiate API clients based on the endpoint they’re connecting to, such as Twitter, Facebook, or Google services.
- Game Development: In game design, a factory can be used to instantiate game entities like enemies, weapons, or power-ups, especially in complex games where entities might have various properties or behaviors based on the game’s state or player choices.
- Web Frameworks: Web frameworks often use factories to instantiate controllers, views, or models based on incoming requests or routes.
- Object Relational Mapping (ORM) Tools: ORMs like Entity Framework in C# or Hibernate in Java use factories to produce objects from database rows based on the database schema and the requested entity type.
- Cloud Service Providers: In applications that utilize various cloud services, factories can instantiate service clients tailored to the particular cloud provider, such as AWS, Azure, or Google Cloud.
- Device Drivers: A factory might load the correct driver or communication protocol for a specific device or device version in software that communicates with different hardware devices.
- File Parsers: In software with various file formats, a factory can determine and instantiate the appropriate parser, e.g., XML, JSON, or YAML.
These are just a few real-time applications of the Factory Design Pattern. Its primary value comes from providing a mechanism to instantiate objects whose specific type might not be known until runtime and centralizing and encapsulating the creation logic for these objects.
Advantages and Disadvantages of Factory Design Pattern in C#
Like all design patterns, the Factory Design Pattern has advantages and disadvantages. Let’s explore both aspects:
Advantages of Factory Design Pattern in C#:
- Decoupling: The Factory Design Pattern promotes decoupling. The client code is decoupled from the specific classes it needs to instantiate, which helps achieve the principle of “separation of concerns.”
- Flexibility: It offers flexibility in terms of which class to instantiate. If we need to change the instantiation logic or introduce new classes, we only need to change the factory, leaving the client code untouched.
- Consistent Object Creation: Having a central place for object creation ensures that all objects are created consistently, following the same rules or configurations.
- Single Responsibility Principle: By delegating the responsibility of object creation to factories, the main business logic classes can focus on their primary responsibilities.
- Easy to Test: Factories can facilitate testing. During unit testing, we can easily mock objects or swap real implementations with stubs.
- Code Reusability: Factories can be reused across different application parts, ensuring the same object creation logic is employed everywhere.
Disadvantages of Factory Design Pattern in C#:
- Complexity: Introducing factories can sometimes over-complicate the design, especially if not genuinely required. This can make the codebase harder to understand for newcomers.
- Maintenance: A factory can become a maintenance nightmare if not designed carefully. For example, a factory with a large switch-case statement can become hard to manage and modify.
- Abstraction Overhead: While abstractions like factories can make the system more flexible, they also introduce a level of indirection. This might have a slight performance overhead (though, in most cases, it’s negligible) and can sometimes make debugging more challenging.
- Overhead of Number of Classes: A concrete factory class might need to be created for each product. This can increase the number of classes, making the codebase larger and possibly harder to navigate.
- Misuse: Like any other pattern, there’s a danger of misuse. Using the Factory Design Pattern where it’s unnecessary can lead to unnecessary complexity.
In conclusion, while the Factory Design Pattern offers several benefits, especially regarding flexibility and decoupling, it’s crucial to weigh these against the potential drawbacks. It’s essential to use it judiciously and only when its advantages outweigh the disadvantages in the specific context of your application.
In the next article, I will discuss the Real-time Examples of the Factory Design Pattern in C#. Here, in this article, I try to explain the Factory Design Pattern in C# with Examples. I hope this article will help you with your needs. I would like to have your feedback. Please post your feedback, questions, or comments about this article.
I have one doubt ..
If one new client want one more method like GetAccountDetails()
What will be the approach??
any way awesome article..really helped a lot to understand..
It’s very logical question
Interface segregation
not related at all.
ISP: https://dotnettutorials.net/lesson/interface-segregation-principle/
its about make interface thicker, not wider.
This is not possible with Factory Design Pattern. For that purpose, you need to use Interface Segregation Principle.
https://dotnettutorials.net/lesson/interface-segregation-principle/
Hi there,
I wanted to share my thoughts on the factory pattern and interface segregation principle. I think that in some cases, it may not be necessary to define an interface and let a class implement the definition.
In order to achieve this scenario, our factory should not be an interface. Instead, we can replace it with an abstract class and let the derived class choose whether it needs to implement the GetAccountDetails() method specific to client needs. Other classes may ignore this method if it has been defined as abstract in the base class.
This way, we can achieve the factory pattern without the need for interface segregation principle for just one method. I believe this approach can simplify our code and make it more flexible.
Suman,adding GetAccountDetails() should be added to the interface Credit Card classed will be compelled to implement it,
Good and Simple to understand.
BEST
Good work. 🙂
Awesome Article
Excellent transmission of knowledge regarding design patterns in c #.
Awesome article really its helping a lot to understand the actual concept.
Simply Awesome!!
Very nice e.g.
Thank you for this course.
you have managed to make the course very easy for everyone
I really want to read the next lesson
Excellent article in a very great detail ,really helped me to understand in
Best article encountered on the topic.
Simple and meaningful explanation.
really thanks for the good examples. usually, I see everyone try to tell you these design patterns but with not real world examples. so it takes more time to learn.
cool
thanks
Very nicely documented article
very simple and real usecase the way of putting the things is wonderful.
Very clear explanation and detailed information, thank you for shering. 🙂
Best I have ever read!
Thanks it helped to understand
Very nicely explained.
Thank you , very well written and was so easy to understand ,was trying to understand design patterns for long but couldn’t understand it properly from the videos and articles I had gone through so far .
Nice
I really liked design pattern explanation . is it possible to get PDF file or any paid pdf file available
We don’t have a PDF Version of the Document. Everything is Free and Available on our website. On a regular basis, we are updating the content and give more suitable examples to understand the concept. Please keep visiting and keep sharing your valuable suggestions.
What if this pattern is mixed with Dependence Injection or reflection? In the first case to create an object, in the second case to search and build a list of objects in the library at startup ?