Factory Pattern in C#: Simplifying Object Creation
The Factory Pattern is one of the creational design patterns used in software development. It helps in managing the object creation process by abstracting it from the client code. Instead of instantiating objects directly in the client code, the Factory Pattern allows the client to request objects without needing to know the exact class type or logic behind the object creation. This results in more maintainable, flexible, and scalable code.
The Problem Without Factory Pattern
Before diving into the Factory Pattern, let’s first see what happens without it.
Code Example:
namespace factoryPatternexample
{
public interface IMobile
{
string GetCPU();
string GetRAM();
}
}
namespace factoryPatternexample
{
public class ApplePhone : IMobile
{
public string GetCPU()
{
return "Apple CPU";
}
public string GetRAM()
{
return "8GB";
}
}
}
namespace factoryPatternexample
{
public class SamsungPhone : IMobile
{
public string GetCPU()
{
return "Samsung CPU";
}
public string GetRAM()
{
return "16GB";
}
}
}
Client Code:
using factoryPatternexample;
string PhoneType = "Samsung";
IMobile mobile;
switch (PhoneType)
{
case "Apple":
mobile = new ApplePhone();
break;
case "Samsung":
mobile = new SamsungPhone();
break;
default:
throw new Exception("Invalid phone type");
}
Console.WriteLine("CPU: " + mobile.GetCPU());
Console.WriteLine("RAM: " + mobile.GetRAM());
Console.ReadLine();
Explanation:
In this code, we have an IMobile interface with two concrete implementations: ApplePhone and SamsungPhone. The client code decides which phone object to instantiate based on the PhoneType string.
Drawbacks of This Approach:
- Tight Coupling: The client code directly depends on the specific classes (ApplePhone and SamsungPhone). Any changes to these classes or the addition of new classes will require changes in the client code.
- Violation of Open/Closed Principle: If we want to add a new phone type, we will need to modify the switch statement, meaning the code is not closed for modification.
The Factory Pattern addresses these issues by centralizing the object creation process in a factory class, making the client code unaware of the specific implementations. The factory class takes care of creating the right object based on input.
Code After Using Factory Pattern:
namespace factoryPatternexample
{
public class MobileFactory
{
public static IMobile GetPhoneObj(string PhoneType)
{
IMobile mobile = null;
switch (PhoneType)
{
case "Apple":
mobile = new ApplePhone();
break;
case "Samsung":
mobile = new SamsungPhone();
break;
default:
throw new Exception("Invalid phone type");
}
return mobile;
}
}
}
Revised Client Code:
using factoryPatternexample;
string PhoneType = "Samsung";
IMobile mobile = MobileFactory.GetPhoneObj(PhoneType);
Console.WriteLine("CPU: " + mobile.GetCPU());
Console.WriteLine("RAM: " + mobile.GetRAM());
Console.ReadLine();
Explanation:
With the Factory Pattern in place, the client code no longer creates objects directly. Instead, it calls the MobileFactory.GetPhoneObj() method to get the required object. The responsibility of creating the objects is now transferred to the MobileFactory class.
Advantages of Using the Factory Pattern:
- Loose Coupling: The client code is no longer dependent on specific classes (ApplePhone or SamsungPhone). It only interacts with the IMobile interface.
- Scalability: Adding a new type of phone (e.g., OnePlusPhone) will not require changes in the client code. You only need to update the factory class.
- Maintainability: The object creation logic is now centralized in one place (the factory), making the code easier to maintain and update.
For example, let’s say we add a new phone class called OnePlusPhone:
namespace factoryPatternexample
{
public class OnePlusPhone : IMobile
{
public string GetCPU()
{
return "OnePlus CPU";
}
public string GetRAM()
{
return "12GB";
}
}
}
Now, the factory class needs a slight update:
namespace factoryPatternexample
{
public class MobileFactory
{
public static IMobile GetPhoneObj(string PhoneType)
{
IMobile mobile = null;
switch (PhoneType)
{
case "Apple":
mobile = new ApplePhone();
break;
case "Samsung":
mobile = new SamsungPhone();
break;
case "OnePlus":
mobile = new OnePlusPhone();
break;
default:
throw new Exception("Invalid phone type");
}
return mobile;
}
}
}
Now, the client can request a OnePlusPhone by simply passing the string "OnePlus" without changing anything in the client-side logic:
string PhoneType = "OnePlus";
IMobile mobile = MobileFactory.GetPhoneObj(PhoneType);
Conclusion:
The Factory Pattern encapsulates object creation and abstracts it from the client code. By using this pattern, you can make your code more maintainable, loosely coupled, and easily scalable. Whenever you find that your code is tightly coupled with multiple concrete classes, consider implementing the Factory Pattern to centralize the creation logic.
Comments
Post a Comment