Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 852
mameesh Avatar asked Sep 21 '16 15:09

mameesh


People also ask

Which class in factory design pattern can be an interface?

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.

What is Factory Method design pattern?

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.

What are the types of factory pattern?

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.

What is the factory method pattern explain with examples?

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.


2 Answers

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));
like image 107
Fabjan Avatar answered Nov 08 '22 07:11

Fabjan


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));
like image 29
Groo Avatar answered Nov 08 '22 08:11

Groo