How Better to Resolve Dependencies in Object Created by Factory

How better to resolve dependencies in object created by factory?

For dependencies that are independent of the instance you're creating, inject them into the factory and store them until needed.

For dependencies that are independent of the context of creation but need to be recreated for each created instance, inject factories into the factory and store them.

For dependencies that are dependent on the context of creation, pass them into the Create method of the factory.

Should I choose Dependency Injection or Factory Pattern

Lets look at the difference between them.

With DI, objects are created externally and "injected" for use by the client object.
Injection is commonly done via constructor.

However, in complex situations, its common to inject a DI container to create the dependency objects, including all sub-dependencies. So it then looks like Abstract Factory!

With Abstract Factory, an instance of a concrete Factory class is injected and the dependency objects are instantiated by the client object.

So DI and Abstract Factory are pretty much the same, when you consider that in both cases, factory objects are passed to the client to enable it to create its dependencies.

In simple cases only, the dependent objects are created externally and passed to the client object. This is how Strategy Pattern works.

Since DI containers are so prevalent now, and used so extensively in frameworks, they have effectively replaced Abstract Factory, at least as a pattern that is frequently spoken about. DI containers are vastly more complex entities than were ever envisaged for Abstract Factories (I believe).

So there is no best practice!

Using a Strategy and Factory Pattern with Dependency Injection

There are a few ways of doing this, but the way I prefer is to inject a list of available strategies into your factory, and then filtering them to return the one(s) you're interested in.

Working with your example, I'd modify IShippingStrategy to add a new property:

public interface IShippingStrategy
{
int CalculateShippingCost(Order order);
string SupportedShippingMethod { get; }
}

Then I'd implement the factory like so:

public class ShippingStrategyFactory : IShippingStrategyFactory
{
private readonly IEnumerable<IShippingStrategy> availableStrategies;

public ShippingStrategyFactory(IEnumerable<IShippingStrategy> availableStrategies)
{
this.availableStrategies = availableStrategies;
}

public IShippingStrategy GetShippingStrategy(Order order)
{
var supportedStrategy = availableStrategies
.FirstOrDefault(x => x.SupportedShippingMethod == order.ShippingMethod);
if (supportedStrategy == null)
{
throw new InvalidOperationException($"No supported strategy found for shipping method '{order.ShippingMethod}'.");
}

return supportedStrategy;
}
}

The main reason I like using it this way is that I never have to come back and modify the factory. If ever I have to implement a new strategy, the factory doesn't have to be changed. If you're using auto-registration with your container, you don't even have to register the new strategy either, so it's simply a case of allowing you to spend more time writing new code.

Factory method with DI and IoC

Having a switch case statement inside of a factory is a code smell. Interestingly, you don't seem to be focusing on solving that issue at all.

The best, most DI friendly solution for this scenario is the strategy pattern. It allows your DI container to inject the dependencies into the factory instances where they belong, without cluttering up other classes with those dependencies or resorting to a service locator.

Interfaces

public interface ICarFactory
{
ICar CreateCar();
bool AppliesTo(Type type);
}

public interface ICarStrategy
{
ICar CreateCar(Type type);
}

Factories

public class Car1Factory : ICarFactory
{
private readonly IDep1 dep1;
private readonly IDep2 dep2;
private readonly IDep3 dep3;

public Car1Factory(IDep1 dep1, IDep2 dep2, IDep3 dep3)
{
this.dep1 = dep1 ?? throw new ArgumentNullException(nameof(dep1));
this.dep2 = dep2 ?? throw new ArgumentNullException(nameof(dep2));
this.dep3 = dep3 ?? throw new ArgumentNullException(nameof(dep3));
}

public ICar CreateCar()
{
return new Car1(this.dep1, this.dep2, this.dep3);
}

public bool AppliesTo(Type type)
{
return typeof(Car1).Equals(type);
}
}

public class Car2Factory : ICarFactory
{
private readonly IDep4 dep4;
private readonly IDep5 dep5;
private readonly IDep6 dep6;

public Car2Factory(IDep4 dep4, IDep5 dep5, IDep6 dep6)
{
this.dep4 = dep4 ?? throw new ArgumentNullException(nameof(dep4));
this.dep5 = dep5 ?? throw new ArgumentNullException(nameof(dep5));
this.dep6 = dep6 ?? throw new ArgumentNullException(nameof(dep6));
}

public ICar CreateCar()
{
return new Car2(this.dep4, this.dep5, this.dep6);
}

public bool AppliesTo(Type type)
{
return typeof(Car2).Equals(type);
}
}

Strategy

public class CarStrategy : ICarStrategy
{
private readonly ICarFactory[] carFactories;

public CarStrategy(ICarFactory[] carFactories)
{
this.carFactories = carFactories ?? throw new ArgumentNullException(nameof(carFactories));
}

public ICar CreateCar(Type type)
{
var carFactory = this.carFactories
.FirstOrDefault(factory => factory.AppliesTo(type));

if (carFactory == null)
{
throw new InvalidOperationException($"{type} not registered");
}

return carFactory.CreateCar();
}
}

Usage

// I am showing this in code, but you would normally 
// do this with your DI container in your composition
// root, and the instance would be created by injecting
// it somewhere.
var strategy = new CarStrategy(new ICarFactory[] {
new Car1Factory(dep1, dep2, dep3),
new Car2Factory(dep4, dep5, dep6)
});

// And then once it is injected, you would simply do this.
// Note that you could use a magic string or some other
// data type as the parameter if you prefer.
var car1 = strategy.CreateCar(typeof(Car1));
var car2 = strategy.CreateCar(typeof(Car2));

Note that because there is no switch case statement, you can add additional factories to the strategy without changing the design, and each of those factories can have their own dependencies that are injected by the DI container.

var strategy = new CarStrategy(new ICarFactory[] {
new Car1Factory(dep1, dep2, dep3),
new Car2Factory(dep4, dep5, dep6),
new Car3Factory(dep7, dep8, dep9)
});

var car1 = strategy.CreateCar(typeof(Car1));
var car2 = strategy.CreateCar(typeof(Car2));
var car3 = strategy.CreateCar(typeof(Car3));

Abstract factories when using dependency injection frameworks

Many containers support factories in one or another way and this is the way you should go.

E.g. Taking your example define a ISessionFactory interface like this

public interface ISessionFactory
{
ISession CreateSession(string name);
}

For Ninject 2.3 see https://github.com/ninject/ninject.extensions.factory and let it be implemented by Ninject

Bind<ISessionFactory>().AsFactory();

For 2.2 do the implementation yourself

public class SessionFactory : ISessionFactory
{
private IKernel kernel;
public SessionFactory(IKernel kernel)
{
this.kernel = kernel;
}

public ISession CreateSession(string name)
{
return this.kernel.Get<ISession>(new ConstructorArgument("name", name));
}
}

DDD - Factory Method and Dependency Injection

I responded to this on the DDD list where you asked it as well, but I'll repeat my concerns here too.

I think you're getting the roles of a Factory and a Repository confused here.

Your methods named, for example, CreateCurrencyFromAlphaCode, actually look like they should be, for example, FindCurrencyByAlphaCode and they should exist in a Repository, not in a Factory. A Factory is used for creating objects that don't already exist in your persistence layer (or for instantiating objects from data you've already received from your persistence layer).

Also, your AlphaCode actually sounds like an Identity, so, if you did want to continue retrieving Currencies from your persistence layer, then I'd suggest your Currency is most likely not a Value Object, but rather an Entity.

Without knowing more about your domain, it's hard to know if this is a correct design decision or not. I'm inclined to think that duffymo's response would serve you better and then you wouldn't need to worry about fetching from a data store each time at all.



Related Topics



Leave a reply



Submit