I read some tutorials about a the factory an abstract factory pattern and saw some examples of it. In one of the tutorials i read that the factory pattern could replace major "if" or "switch case" statements and follows the open/closed (solid) principles.
In one of my projects a have a huge "switch case" which i wanna replace by a(n) (abstract) factory. It's already interface based so implementing an factory shouldn't be that difficult but in all the examples i read in tutorials the factory produced a single concrete type based on configuration. Can anyone point me in the right direction how to implement a factory that could produce multiple types based on an enum that follows the Solid principles an replaces the large "switch case"....or am I misinformed and is the "switch case" moved to the factory?
code at this moment:
public interface ISingleMailProcessor : IMailProcessor
{
MailResult ProcesMail(Mail mail);
}
public MailResult GetMailResult(mail)
{
ISingleMailProcessor mailprocessor;
switch (mail.MailType)
{
case MailConnector.MailType.AcronisBackup:
mailprocessor = new AcronisProcessor();
return mailprocessor.ProcesMail(mail);
case ......etc.
}
}
What you have there implemented is the Strategy pattern which is still a valid approach. Anyway, if you want to replace the whole switch and to make it more maintainable you can use this
public interface IProcessMail
{
bool CanProcess(MailType type);
MailResult GetMailResult(Mail mail);
}
Each mail processor will implement this interface. Then you'll have this
public class MailProcessorExecutor
{
public MailProcessorSelector(IEnumerable<IProcessMail> processors)
{
_processors=processors;
}
public MailResult GetResult(Mail mail)
{
var proc=_processor.FirstOrDefault(p=>p.CanProcess(mail.MailType));
if (proc==null)
{
//throw something
}
return proc.GetMailResult(mail);
}
static IProcessMail[] _procCache=new IProcessMail[0];
public static void AutoScanForProcessors(Assembly[] asms)
{
_procCache= asms.SelectMany(a=>a.GetTypesDerivedFrom<IProcessMail>()).Select(t=>Activator.CreateInstance(t)).Cast<IProcessMail>().ToArray();
}
public static MailProcessorExecutor CreateInstance()
{
return new MailProcessorExecutor(_procCache);
}
}
//in startup/config
MailProcessorExecutor.AutoScanForProcessors([assembly containing the concrete types]);
//usage
var mailProc=MailProcessorExecutor.CreateInstance();
var result=mailProc.GetResult(mail);
Ok, so, the point is there will be an object in charge of selecting and executing the processors. The static methods are optional, the AutoScan
method searches any given assemblies for concrete IPocessMail
implementations then instantiates them. This allows you to add/remove any processor and it will be used automatically (no other setup required). Also there is no switch involved and there will never be. Note that GetTypesFromDerived
is a helper method I use (it's part of my CavemanTools library) and the Activator requires a parameterless constructor. Instead of it you can use a DI Container to get the instances (if you're using one or the processors have deps)
If you're using a DI Container, you don't need the CreateInstance
method. Just register MailProcessorExecutor
as a singleton and inject it (pass it as a constructor argument) where you need it.