Jeffrey Palermo recently posted a blog post titled Constructor over-injection anti-pattern – go read his post first if you want to be able to follow my arguments.
His point seems to be that Constructor Injection can be an anti-pattern if applied too much, particularly if a consumer doesn't need a particular dependency in the majority of cases.
The problem is illustrated in this little code snippet:
bool isValid = _validator.Validate(order);
if (isValid)
{
_shipper.Ship(order);
}
If the Validate method returns false often, the shipper dependency is never needed.
This, he argues, can lead to inefficiencies if the dependency is costly to create. It's not a good thing to require a costly dependency if you are not going to use it in a lot of cases.
That sounds like a reasonable statement, but is it really? And is the proposed solution a good solution?
No, this isn't a reasonable statement, and the proposed solution isn't a good solution.
It would seem like there's a problem with Constructor Injection, but in reality the problem is that it is being used incorrectly and in too constrained a way.
The proposed solution is problematic because it involves tightly coupling the code to OrderShipperFactory. This is more or less a specialized application of the Service Locator anti-pattern.
Consumers of OrderProcessor have no static type information to warn them that they need to configure the OrderShipperFactory.CreationClosure static member - a completely unrelated type. This may technically work, but creates a very developer-unfriendly API. IntelliSense isn't going to be of much help here, because when you want to create an instance of OrderProcessor, it's not going to remind you that you need to statically configure OrderShipperFactory first. Enter lots of run-time exceptions.
Another issue is that he allows a concrete implementation of an interface to change the design of the OrderProcessor class - that's hardly in the spirit of the Liskov Substitution Principle. I consider this a strong design smell.
One of the commenters (Alwin) suggests instead injecting an IOrderShipperFactory. While this is a better option, it still suffers from letting a concrete implementation influence the design, but there's a better solution.
First of all we should realize that the whole case is a bit construed because although the IOrderShipper implementation may be expensive to create, there's no need to create a new instance for every OrderProcessor. Instead, we can use the so-called Singleton lifetime style where we share or reuse a single IOrderShipper instance between multiple OrderProcessor instances.
The beauty of this is that we can wait making that decision until we wire up the actual dependencies. If we have implementations of IOrderShipper that are inexpensive to create, we may still decide to create a new instance every time.
There may still be a corner case where a shared instance doesn't work for a particular implementation (perhaps because it's not thread-safe). In such cases, we can use Lazy loading to create a LazyOrderShipper like this (for clarity I've omitted making this implementation thread-safe, but that would be trivial to do):
public class LazyOrderShipper : IOrderShipper
private OrderShipper shipper;
#region IOrderShipper Members
public void Ship(Order order)
if (this.shipper == null)
this.shipper = new OrderShipper();
this.shipper.Ship(order);
#endregion
Notice that this implementation of IOrderShipper only creates the expensive OrderShipper instance when it needs it.
Instead of directly injecting the expensive OrderShipper instance directly into OrderProcessor, we wrap it in the LazyOrderShipper class and inject that instead. The following test proves the point:
[TestMethod]
public void OrderProcessorIsFast()
// Fixture setup
var stopwatch = new Stopwatch();
stopwatch.Start();
var order = new Order();
var validator = new Mock<IOrderValidator>();
validator.Setup(v =>
v.Validate(order)).Returns(false);
var shipper = new LazyOrderShipper();
var sut = new OrderProcessor(validator.Object,
shipper);
// Exercise system
sut.Process(order);
// Verify outcome
stopwatch.Stop();
Assert.IsTrue(stopwatch.Elapsed <
TimeSpan.FromMilliseconds(777));
Console.WriteLine(stopwatch.Elapsed);
// Teardown
This test is significantly faster than 777 milliseconds because the OrderShipper never comes into play. In fact, the stopwatch instance reports that the elapsed time was around 3 ms!
The bottom line is that Constructor Injection is not an anti-pattern. On the contrary, it is the most powerful DI pattern available, and you should think twice before deviating from it.
Remember Me
a@href@title, b, em, i, strike, strong
Page rendered at Saturday, February 04, 2012 7:50:12 PM (Romance Standard Time, UTC+01:00)
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.