My previous post led to this comment by Phil Haack:

Your LazyOrderShipper directly instantiates an OrderShipper. What about the dependencies that OrderShipper might require? What if those dependencies are costly?

I didn't want to make my original example more complex than necessary to get the point across, so I admit that I made it a bit simpler than I might have liked. However, the issue is easily solved by enabling DI for the LazyOrderShipper itself.

As always, when the dependency's lifetime may be shorter than the consumer, the solution is to inject (via the constructor!) an Abstract Factory, as this modification of LazyOrderShipper shows:

public class LazyOrderShipper2 : IOrderShipper
{
    private readonly IOrderShipperFactory factory;
    private IOrderShipper shipper;
 
    public LazyOrderShipper2(IOrderShipperFactory factory)
    {
        if (factory == null)
        {
            throw new ArgumentNullException("factory");
        }
 
        this.factory = factory;
    }
 
    #region IOrderShipper Members
 
    public void Ship(Order order)
    {
        if (this.shipper == null)
        {
            this.shipper = this.factory.Create();
        }
        this.shipper.Ship(order);
    }
 
    #endregion
}

But, doesn't that reintroduce the OrderShipperFactory that I earlier claimed was a bad design?

No, it doesn't, because this IOrderShipperFactory doesn't rely on static configuration. The other point is that while we do have an IOrderShipperFactory, the original design of OrderProcessor is unchanged (and thus blissfully unaware of the existence of this Abstract Factory).

The lifetime of the various dependencies is completely decoupled from the components themselves, and this is as it should be with DI.

This version of LazyOrderShipper is more reusable because it doesn't rely on any particular implementation of OrderShipper - it can Lazily create any IOrderShipper.


Comments

We could indicate optional dependencies by using Func<TResult> - http://msdn.microsoft.com/en-us/library/bb534960.aspx - delegates for the given constructor parameter. The given DI framework could be configured to resolve any Func<TResult> as x => Container.Resolve<TResult>()

This way the lazy/optional nature of the parameter is obvious to clients of the class, and there is no need to generate lazy implementation classes manually.

Note: I haven't had any practical experience using DI frameworks, so the above might not be possible at all :)
2010-01-20 20:44 UTC
Yes, Func<T> is sometimes a viable option. In general, I consider delegates to be anonymous interfaces, so Func<T> is really just an Abstract Factory. In other words, IOrderShipperFactory is functionally equivalent to Func<IOrderShipper>.

I had a period where I used a lot of delegates as injected dependencies, but I have more or less abandonded that approach again. While it technically works fine, it makes unit testing a bit harder because it's harder to test that a given object contains a specific type of Strategy if it's just a Func<T> or similar.

In any case, I'm mostly familiar with Castle Windsor at the moment. Although I have yet to try it out, I think the new Typed Factory Facility fits the bill very well - with that, we would never have to code a real implementation of IOrderShipperFactory because Windsor would be able to dynamically emit one for us.
2010-01-20 21:56 UTC
I have the feeling I did not set the context. Let me do that, and tell me if I the issues you raised still hold - both of them are important!

What I meant to propose is that we change Jeffrey Palermo's original example like the below:
- private readonly IOrderShipper _shipper;
+ private readonly Func< IOrderShipper > _shipperFactory;


- public OrderProcessor(IOrderValidator validator, IOrderShipper shipper)
+ public OrderProcessor(IOrderValidator validator, Func< IOrderShipper > shipperFactory)

- _shipper = shipper;
+ _shipperFactory = shipperFactory;

- _shipper.Ship(order);
+ _shipperFactory().Ship(order);

The change to the tests should be straightforward as well,

- new OrderProcessor(validator, mockShipper)
+ new OrderProcessor(validator, () => mockShipper)
2010-01-20 22:29 UTC
I'm surprised no one has mentioned .NET 4's Lazy<T>.

To communicate intent, it's clearer than Func<T>:

public UsesOrderShipper(Lazy<IOrderShipper> orderShipper)

There's a more complete example using Lazy<T> with Autofac.

Cheers,
Nick
2010-01-21 10:59 UTC
To be fair, Alwin mentioned it over on Jeffrey Palermo's original post before I posted my response.

That would definitely be an option as well, but I rather wanted to show the route involving absolutely no redesign of the original OrderProcess, and I couldn't do that purely with Lazy<IOrderShipper>. The most important point I wanted to make is that you can solve this problem using basic tools available since .NET 1.0.

It would, however, make a lot of sense to implement LazyOrderShipper by injecting Lazy<IOrderShipper> into it instead of inventing a new IOrderShipperFactory interface.
2010-01-21 17:47 UTC
I like the Func&lt;T&gt; and Lazy&lt;T&gt; solutions for addressing any real performance issues, but based upon the original example I still submit that the cleanest approach would be to just register the type with a singleton lifestyle to begin with. After the first two valid orders, it's more efficient.
2010-01-22 15:25 UTC
@Derek Greer: Aggreed, and that was also my initial point in my previous post.
2010-01-22 23:54 UTC
Thomas #
Mark,

While it's easy to get it work with Typed Factory Facility and Castle, how do you implement the factory :

- without static configuration ?
- without passing the container in ?

Or I missed something ?

Thanks,

Thomas
2012-03-14 21:52 UTC
Thomas, who said anything about a DI Container or Castle Windsor for that matter?
2012-03-15 06:47 UTC
Thomas #
Mark,

I was refering to your first comment. If I have no problem with the pattern I would like to know how you would do from the implementation point of view.

Thanks,

Thomas
2012-03-15 08:50 UTC
Ah, sorry... I'm not sure I entirely understand the question. With Windsor's Typed Factory Facility, you'd reqister the IOrderShipperFactory as being auto-generated by Windsor. I can't recall the exact syntax for this right now, but that registration would happen as part of the Registration phase of RRR.
2012-03-15 09:26 UTC
Thomas #
Mark,

With Windsor there is no problem as TypedFactoryFacility provides implementation on the fly. However if you take another container you have to provide the implementation of IOrderShipperFactory on your own. Now the question is. How my implementation of the factory will pull the IOrderShipper implementation from the container ? I see two choices :

- configure staticaly (like Jeffrey did in his post)
- pass the container into the factory that it could resolve IOrderShipper.
- third choice that I don't know :)

I hope it's clearer now. Let me know if it doesn't make sense.

Thanks,

Thomas
2012-03-15 10:04 UTC
Thomas, I wrote a new blog post to answer your question.

HTH
2012-03-15 21:04 UTC
Thomas #
Thanks Mark, I go to read it :)
2012-03-16 14:11 UTC


Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or somewhere else with a permalink. Ping me with the link, and I may respond.

Published

Wednesday, 20 January 2010 18:08:36 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!
Published: Wednesday, 20 January 2010 18:08:36 UTC