I have 1 interface:
public interface ISummary
{
int EventId {get; set;}
}
And many concrete classes that implement this interface:
public class EmployeeSummary : ISummary
{
public int EventId {get; set;},
public int TotalUniqueCount {get; set;}
public int Location {get; set;}
}
public class CarSummary : ISummary
{
public int EventId {get; set;}
public int TotalMiles {get; set;}
public int TotalHours {get; set;}
}
etc....
The only shared property is the EventId
. Is there a way to have 1 factory method that creates all of these summary objects? I want 1 entry point which decides which objects to create.
So something like:
public ISummary CreateSummary(ConcreteObjectType with properties)
{
if EmployeeSummary
--Call this method to create and return EmployeeSummary
if CarSummary
--Call this method create and return CarSummary
}
I want all calls within other classes to call this method rather than creating the objects themselves.
The part I am struggling with is how do I pass the properties to assign to the objects to this CreateSummary
method since all the properties on the objects will be different?
I am open to changing the objects at this point at well if there is a better design pattern I should be using here.
The factory design pattern says that define an interface ( A java interface or an abstract class) for creating object and let the subclasses decide which class to instantiate. The factory method in the interface lets a class defers the instantiation to one or more concrete subclasses.
The factory method is a creational design pattern, i.e., related to object creation. In the Factory pattern, we create objects without exposing the creation logic to the client and the client uses the same common interface to create a new type of object.
We also discussed their four different types, i.e., Singleton, Factory Method, Abstract Factory and Builder Pattern, their advantages, examples and when should we use them.
Example. The Factory Method defines an interface for creating objects, but lets subclasses decide which classes to instantiate. Injection molding presses demonstrate this pattern. Manufacturers of plastic toys process plastic molding powder, and inject the plastic into molds of the desired shapes.
Well, that's exactly why Factory Method pattern exists :
public class SummaryFactory
{
// new instance with values assigned by action delegate or default
public T Create<T>(Action<T> action = null) where T : ISummary, new()
{
var result = new T();
action?.Invoke(result);
return result;
}
// with object to assign value from (map)
public T Create<T>(object map) where T : ISummary, new()
{
var result = new T();
PropertyInfo[] props = map.GetType().GetProperties();
PropertyInfo[] tProps = typeof(T).GetProperties();
foreach (var prop in props)
{
var upperPropName = prop.Name.ToUpper();
var foundProperty = tProps.FirstOrDefault(p => p.Name.ToUpper() == upperPropName);
foundProperty?.SetValue(result, prop.GetValue(map));
}
return result;
}
// new instance without generic parameters
public object Create(Type type)
{
var instance = Activator.CreateInstance(type);
// add some other logic that changes instance
return instance;
}
}
And now you can use this factory :
var factory = new SummaryFactory();
var carSummary = factory.Create<CarSummary>();
var carSummary2 = factory.Create<CarSummary>(car => { car.TotalMiles = 50; });
var carSummary3 = factory.Create<CarSummary>(new { TotalMiles = 50 });
var employeeSummary = factory.Create(typeof(EmployeeSummary));
Generics with a new()
constraint are only making tighter coupling. If you already need know which type to pass as the generic parameter T
, and you know what the method will return, what's the point in using it then?
// This is a *helper method* which will create a car summary.
// No abstraction, no polymorphism, just a template helper method.
var summary = CreateSummary<CarSummary>(carData);
If you need an abstract factory, then it means your calling method only knows the return interface, and it's the factory which is supposed to decide on the actual implementation.
In your case, (I believe) you have several types of data classes, and you want the method to create the appropriate summary for each type. I.e. something like:
class EmployeesData : IEmployeesData
{ ... }
class CarsData : ICarsData
{ ... }
// at this point we don't know what `data` actually is
IData data = GetDataFromSomewhere();
// so we cannot pass the concrete generic parameter here
ISummary summary = GetSummary(data);
Then what you need is some sort of a strategy which will choose the right implementation at runtime. Strategies for different input types should be registered at program startup (somewhere inside the composition root, or injected through DI), so that you have something like:
static readonly Dictionary<Type, Func<object, ISummary>> _strategies =
new Dictionary<Type, Func<object, ISummary>>();
public static void Register<T>(Func<T, ISummary> strategy)
{
_strategies[typeof(T)] = input => strategy((T)input);
}
public ISummary CreateSummary(object input)
{
var type = input.GetType();
Func<object, ISummary> strategy;
if (!_strategies.TryGetValue(type, out strategy))
throw new ArgumentException($"No strategy registered for type {type}");
return strategy(input);
}
So, somewhere at your composition root you would have the concrete methods:
ISummary CreateEmployeeSummary(EmployeesData data)
{ ... }
ISummary CreateCarSummary(CarsData data)
{ ... }
// and finally register all concrete methods:
Register<IEmployeesData>(d => CreateEmployeeSummary(d));
Register<ICarsData>(d => CreateCarsSummary(d));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With