Search code examples
c#design-patternsfactory-pattern

Factory to create different objects of same interface


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.


Solution

  • 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));