I am working on a side project to better understand Inversion of Control and Dependency Injection and different design patterns.
I am wondering if there are best practices to using DI with the factory and strategy patterns?
My challenge comes about when a strategy (built from a factory) requires different parameters for each possible constructor and implementation. As a result I find myself declaring all possible interfaces in the service entry point, and passing them down through the application. As a result, the entry point must be changed for new and various strategy class implementations.
I have put together a paired down example for illustration purposes below. My stack for this project is .NET 4.5/C# and Unity for IoC/DI.
In this example application, I have added a default Program class that is responsible for accepting a fictitious order, and depending on the order properties and the shipping provider selected, calculating the shipping cost. There are different calculations for UPS, DHL, and Fedex, and each implemnentation may or may not rely on additional services (to hit a database, api, etc).
public class Order
{
public string ShippingMethod { get; set; }
public int OrderTotal { get; set; }
public int OrderWeight { get; set; }
public int OrderZipCode { get; set; }
}
Fictitious program or service to calculate shipping cost
public class Program
{
// register the interfaces with DI container in a separate config class (Unity in this case)
private readonly IShippingStrategyFactory _shippingStrategyFactory;
public Program(IShippingStrategyFactory shippingStrategyFactory)
{
_shippingStrategyFactory = shippingStrategyFactory;
}
public int DoTheWork(Order order)
{
// assign properties just as an example
order.ShippingMethod = "Fedex";
order.OrderTotal = 90;
order.OrderWeight = 12;
order.OrderZipCode = 98109;
IShippingStrategy shippingStrategy = _shippingStrategyFactory.GetShippingStrategy(order);
int shippingCost = shippingStrategy.CalculateShippingCost(order);
return shippingCost;
}
}
// Unity DI Setup
public class UnityConfig
{
var container = new UnityContainer();
container.RegisterType<IShippingStrategyFactory, ShippingStrategyFactory>();
// also register IWeightMappingService and IZipCodePriceCalculator with implementations
}
public interface IShippingStrategyFactory
{
IShippingStrategy GetShippingStrategy(Order order);
}
public class ShippingStrategyFactory : IShippingStrategyFactory
{
public IShippingStrategy GetShippingStrategy(Order order)
{
switch (order.ShippingMethod)
{
case "UPS":
return new UPSShippingStrategy();
// The issue is that some strategies require additional parameters for the constructor
// SHould the be resolved at the entry point (the Program class) and passed down?
case "DHL":
return new DHLShippingStrategy();
case "Fedex":
return new FedexShippingStrategy();
default:
throw new NotImplementedException();
}
}
}
Now for the Strategy interface and implementations. UPS is an easy calculation, while DHL and Fedex may require different services (and different constructor parameters).
public interface IShippingStrategy
{
int CalculateShippingCost(Order order);
}
public class UPSShippingStrategy : IShippingStrategy()
{
public int CalculateShippingCost(Order order)
{
if (order.OrderWeight < 5)
return 10; // flat rate of $10 for packages under 5 lbs
else
return 20; // flat rate of $20
}
}
public class DHLShippingStrategy : IShippingStrategy()
{
private readonly IWeightMappingService _weightMappingService;
public DHLShippingStrategy(IWeightMappingService weightMappingService)
{
_weightMappingService = weightMappingService;
}
public int CalculateShippingCost(Order order)
{
// some sort of database call needed to lookup pricing table and weight mappings
return _weightMappingService.DeterminePrice(order);
}
}
public class FedexShippingStrategy : IShippingStrategy()
{
private readonly IZipCodePriceCalculator _zipCodePriceCalculator;
public FedexShippingStrategy(IZipCodePriceCalculator zipCodePriceCalculator)
{
_zipCodePriceCalculator = zipCodePriceCalculator;
}
public int CalculateShippingCost(Order order)
{
// some sort of dynamic pricing based on zipcode
// api call to a Fedex service to return dynamic price
return _zipCodePriceService.CacluateShippingCost(order.OrderZipCode);
}
}
The issue with the above is that each strategy requires additional and different services to perform the 'CalculateShippingCost' method. Do these interfaces/implementations need to be registered with the entry point (the Program class) and passed down through the constructors?
Are there other patterns that would be a better fit to accomplish the above scenario? Maybe something that Unity could handle specifically (https://msdn.microsoft.com/en-us/library/dn178463(v=pandp.30).aspx)?
I greatly appreciate any help or a nudge in the right direction.
Thanks, Andy
Please see both John H and Silas Reinagel's answers. They are both very helpful.
I ended up doing a combination of both answers.
I updated the factory and interface as John H mentions.
And then in the Unity container, I added the implementations with the new named parameters like Silas Reinagel shows.
I then followed the answer here for using Unity to register the collection for injection into the strategy factory. Way to fill collection with Unity
Now each strategy can be implemented separately without needing to modify upstream.
Thank you all.
Register and resolve them using your Strategy Type strings.
Like this:
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:Then I'd implement the factory like so:
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.
When applying Dependency Injection, we define all the class’s dependencies as required arguments in the constructor. This practice is called Constructor Injection. This pushes the burden of creating the dependency from the class to its consumer. The same rule however applies to the class’s consumers as well. They as well need to define their dependencies in their constructor. This goes all the way up the call stack, and this means that so called 'object graphs' can become quite deep at some points.
Dependency Injection causes the responsibility of creating classes all the way up to the entry point of the application; the Composition Root. This does mean however that the entry point needs to know about all the dependencies. In case we don't use a DI Container -a practice called Pure DI- it means that at this point all dependencies must be created in plain old C# code. In case we use a DI Container we still have to tell the DI container about all dependencies.
Sometimes however we can make use of a technique called batch or Auto-Registration, where the DI Container will use reflection over our projects and register types using Convention over Configuration. This saves us the burden of registering all types one by one and often prevents us from making changes to the Composition Root every time a new class is added to the system.
Absolutely.
The application's entry point is the most volatile part of the system. It always is, even without DI. But with DI we make the rest of the system much less volatile. Again, we can reduce the amount of code changes we need to make at the entry point by applying Auto-Registration.
I would say the best practice concerning factories is to have as little of them as possible, as explained in this article. As a matter of fact, your factory interface is redundant and only complicates the consumers that require it (as explained in the article). Your application can easily do without and you can instead inject a
IShippingStrategy
directly, since this is the only thing the consumer is interested in: to get the shipping cost for an order. It doesn't care whether there is one or dozens of implementations behind it. It just wants to get the shipping cost and go on with its job:This means however that the injected shipping strategy must now be something that can decide how to calculate the cost based on the
Order.Method
property. But there's a pattern for this called the Proxy pattern. Here's an example:This proxy internally acts a bit like a factory, but there are two important differences here:
IShippingStrategy
.This proxy simply forwards the incoming call to an underlying strategy implementation that does the actual work.
There is a variety of ways to implement such proxy. For instance, you could still create the dependencies here manually -or you could forward the call to the container, who will create the dependencies for you. Also the way you inject the dependencies can differ based on what's best for your application.
And even though such proxy might internally work like a factory, the important thing is that there is no factory Abstraction here; that would only complicate the consumers.
Everything discussed above is discussed in more detail in the book Dependency Injection Principles, Practices, and Patterns, by Mark Seemann and myself. For instance, Composition Root is discussed in § 4.1, Constructor Injection in § 4.2, abuse of Abstract Factories in § 6.2, and Auto-Registration in chapter 12.
So I did it like this. I would have preferred to have injected an IDictionary, but because of the limitation with injecting "IEnumerable" into the constructor (this limitation is Unity specific), I came up with a little workaround.
..
..
..
..
..
..
...
And calling code: (that would be in something like "Program.cs" for example)
Logging Output:
So each concrete has a static string providing its name in a strong-typed fashion. ("FriendlyName")
And then I have an instance string-get property which uses the exact same value to keep things in sync. ("FriendlyNameInstance")
By forcing the issue by using a property on the Interface (below partial code)
I can use this to "find" my shipper out of the collection of shippers.
The internal method "FindIShipper" is the kinda-factory, but removes the need to have a separate IShipperFactory and ShipperFactory interface and class. Thus simplifying the overall setup. And still honors Constructor-Injection and Composition root.
If anyone knows how to use
IDictionary<string, IShipper>
(and inject via the constructor), please let me know.But my solution works...with a little razzle dazzle.
...........................
My third-party-dll dependency list. (I'm using dotnet core, but dotnet framework with a semi new version of Unity should work too). (See PackageReference's below)