Factory Design Pattern (Part 1)
Factory design pattern is a creational design pattern, judging by its name it sole purpose is to create (or instantiate) objects, yet another main benefit its you don’t have to worry about the creational process what so ever, by saying “you”, it means that your current context of code can be focus on its own logic instead of other classes issues, in simple terms, when you are using a laptop, you care only about enjoying it as a product, you don’t have to care about its creational process at its “factory”.
There are 3 types of factory patterns: simple factory, factory method (part1) and abstract factory (part2).
Lets dive into an example:
Suppose you are an owner of a protected-by-login website, your customers can register and then should confirm themselves either by SMS or by an Email.
Lets first try to write the user approval code at first I’ll demonstrate the naïve way, and gradually we’ll see how the factory design pattern will improve our code quality, make it more extensible and testable:
public class Registration
{
private readonly User _user;
public Registration(User user)
{
this._user = user;
}
public void SendApproval()
{
ApprovalProvider approvalProvider; if (_user.ApprovalType == "SMS")
{
approvalProvider = new SMSAprovalProvider();
}
else if (_user.ApprovalType == "Email")
{
approvalProvider = new EmailAprovalProvider();
}
aporovalProvider.SendApproval(_user);
}
}public abstract class ApprovalProvider
{
public abstract void SendApproval(User user);
}public class SMSAprovalProvider : ApprovalProvider
{
public SMSAprovalProvider() { }
public override void SendApproval(User user)
{
//Send SMS logic in here...
}
}public class EmailAprovalProvider : ApprovalProvider
{
public EmailAprovalProvider() { }
public override void SendApproval(User user)
{
//Send EMAIL logic in here...
}
}
As you may see in the above code, the SendApproval method is responsible for both instantiating the approvalProvider and both the actual logic for sending, it is violating the single responsibility principle, it is a code harder to maintain and test in real world scenarios the SMS/Email constructors can be a lot more complex (only for the sake of brevity I kept the constructors clean and simple).
Simple Factory Pattern
Our goal is to make the SendApproval method to be unaware of the particular implementation details instantiation of the approval provider.
We will introduce the new object factory, its sole purpose is to handle the approval provider creation (now the factory holds only reference to the approvalType property instead of the all the user object), and it can be reuse now from the entire application:
public class ApprovalProviderFactory
{
public static ApprovalProvider
CreateApprovalProvider(string approvalType)
{
ApprovalProvider approvalProvider;
if (approvalType == "SMS")
{
approvalProvider = new SMSAprovalProvider();
}
else if (approvalType == "Email")
{
approvalProvider = new EmailAprovalProvider();
}
return approvalProvider;
}
}
Now the SendApproval method looks much cleaner and easy to maintain as it is now separated from the creational process of the approval provider:
public class Registration
{
private readonly User _user;
public Registration(User user)
{
this._user = user;
}
public void SendApproval()
{
var aporovalProvider = ApprovalProviderFactory.CreateApprovalProvider(_user.ApprovalType);
aporovalProvider.SendApproval(_user);
}
}
Factory Method Pattern (or “Factory Pattern”)
Now we are not only want to separate our creational object (approvalProvider) from the code logic (SendApproval) as we did in the simple factory, we would also want our creational object to be more extensible.
This is where the plot gets thicker: up until now, we had one Email provider (MailChimp) and one Sms provider (Twilio), now we want to add a second Email provider (SendGrid), so we need to exhibit two new objects: MailChimpEmailAprovalProvider and SendGridEmailAprovalProvider.
Indeed the factory method pattern is much similar to simple factory, the main difference here is by representing a new ApprovalProviderFactory which has by its nature an abstract method of CreateApprovalProvider, by doing this we are allowing other simple sub-classes factories (EmailApprovalProviderFactory and SmsApprovalProviderFactory) to represent their own concrete implementation.
As you may see, we added GetApprovalProvider method which allows us to add further own implementation at the level of the common ApprovalProviderFactory just before returning the insanitation back to the client (this is why its name is Factory Method).
ApprovalProviderFactory:
public abstract class ApprovalProviderFactory
{
public abstract ApprovalProvider CreateApprovalProvider(string approvalType); public ApprovalProvider GetApprovalProvider(string approvalType)
{
var provider = CreateApprovalProvider(approvalType);
// Here you can do whatever you want with the object before it is being returned to the client. return provider;
}
}
The simple sub-classes factories (email, sms):
public class EmailApprovalProviderFactory : ApprovalProviderFactory
{
public override ApprovalProvider CreateApprovalProvider(string approvalType)
{
ApprovalProvider approvalProvider;
if (approvalType == "MailChimp")
{
approvalProvider = new MailChimpEmailAprovalProvider();
}
else if (approvalType == "SendGrid")
{
approvalProvider = new SendGridEmailAprovalProvider();
} return approvalProvider;
}
}public class SmsApprovalProviderFactory : ApprovalProviderFactory
{
public override ApprovalProvider CreateApprovalProvider(string approvalType)
{
return new TwilioSmsApprovalProvider();
}
}
The ApprovalProviders implementations (Twilio, MailChimp, SendGrid):
public abstract class ApprovalProvider
{
public abstract void SendApproval(User user);
}
public class TwilioSmsApprovalProvider : ApprovalProvider
{
public TwilioSmsApprovalProvider() { }
public override void SendApproval(User user)
{
//Send TwilioSMS logic in here...
}
}
public class MailChimpEmailAprovalProvider : ApprovalProvider
{
public MailChimpEmailAprovalProvider() { }
public override void SendApproval(User user)
{
//Send MailChimp EMAIL logic in here...
}
}
public class SendGridEmailAprovalProvider : ApprovalProvider
{
public SendGridEmailAprovalProvider() { }
public override void SendApproval(User user)
{
//Send MailChimp EMAIL logic in here...
}
}
The final usage:
Different from the simple factory we can inject the ApprovalProviderFactory abstract class, now the Registration class does not know which concrete implementation of approval provider it going to use.
Pay attention that we are now using the GetApprovalProvider instead of the abstract CreateApprovalProvider method as we already discussed above.
public class Registration
{
private readonly User _user;
private readonly ApprovalProviderFactory _approvalProviderFactory; public Registration(User user, ApprovalProviderFactory approvalProviderFactory)
{
this._user = user;
this._approvalProviderFactory = approvalProviderFactory;
}public void SendApproval()
{
var aporovalProvider = this._approvalProviderFactory.GetApprovalProvider(_user.ApprovalType);
aporovalProvider.SendApproval(_user);
}
}
To conclude, Moving from concrete instantiations from inside the Registration class into separate factory making our application more:
- Reusable, we can call the abstract factory from different places not only from the Registration class.
- Maintainable, changes to the Registration class or any changes to ApprovalProvider are decoupled and easier to maintain.
- Testable, we can now easily test the SendApproval inside logic method by mocking the approvalProviderFactory.
Stay tuned for “Abstract Factory Pattern” (part2).