4

I'm trying to implement IoC in my app. I have this model:

interface IService;
interface IComponent;

class Service : IService
    Service()

class Component : IComponent
    Component(IService service, object runtimeValue) { }

At some point in my app I need to get a IComponent. My app uses a IoC container (Unity). I can register Service with the container but I can't do the same for Component b/c of its dependency runtimeValue. According to this I have to use a factory and inject that wherever I need to get a IComponent:

interface IComponentFactory
     IComponent CreateComponent(object runtimeValue)

class ComponentProvider : IComponentProvider
     ComponentProvider(IComponentFactory factory) { }

     IComponent CreateAndCacheComponent(object runtimeValue) {
         _component = factory.CreateComponent(runtimeValue)
         return _component
     }

     // other methods

I must be able to register the factory with the container, so it must have only static dependencies. At the same time it must be able to provide a service instance of type IService required to create a component.
Here is the factory implementation. The only thing I could think of was to use a Func<> delegate as dependency:

class ComponentFactory : IComponentFactory
    ComponentFactory(Func<IService> serviceFactoryDelegate)

    IComponent CreateComponent(object runtimeValue) {
        return new Component(serviceFactoryDelegate.Invoke(), runtimeValue)
    }

... and register the delegate with the container as static factory, so that it calls back the container to resolve the service (I'm using Unity 1.2 on .net 2.0):

Container
    .Configure<IStaticFactoryConfiguration>()
    .RegisterFactory<Func<IService>>(container => (Func<IService>)container.Resolve<IService>)

Now I can use the container to resolve a ComponentProvider and get a component based on a runtime value:

// this happens inside CompositionRoot
provider = Container.Resovle<IComponentProvider>()
component = provider.CreateAndCacheComponent("the component")

Now I have some questions about this:

  1. I'm not happy that the factory calls new Component(...). Isn't this poor man's DI?

  2. Does the Hollywood principle still stand when using Func<IService> on factory's constructor? I mean, it ultimately calls container.Resolve<>... kind of like SL. The only difference is the code is in the container registration part of the app rather than inside the factory class.

  3. Is there anything (else) wrong with this implementation, as far as DI and IoC are concerned?

Community
  • 1
  • 1
Suiden
  • 622
  • 4
  • 17
  • Why the need for the delegate? Why can't you inject `IService` directly? – Daniel Hilgarth Oct 13 '11 at 19:49
  • Why do you need the `Func`? Isn't the whole point to allow your container to resolve this? – Jay Oct 13 '11 at 19:50
  • @Daniel, @Jay: Injecting the service assumes the same instance will be used to create the component. If the factory had another method which had to create a different object having a `IService` dependency, then the same service instance would have been used in both methods. Using the delegate allows the container to resolve the service each time it is needed, using its configured lifetime. – Suiden Oct 13 '11 at 20:04
  • You normally shouldn't care about the lifetime of your services or whether you get the same instance as another consumer. Do you *really* need different instances of the service? – Daniel Hilgarth Oct 13 '11 at 20:14
  • Finally I've come across someone else using Funcs like this: http://nblumhardt.com/2010/01/the-relationship-zoo: if A needs to create an instance of B, then it needs a Func (which I assume would call back the container) – Suiden Oct 13 '11 at 21:06
  • @Suiden: As I said, you really should think about whether you really need this. Can you give a real world example in which you really need to have that much control over the service? I am sure there are examples where you really need this, but I am also certain that there aren't too many. As StripplingWarrior said: Maybe, you need to re-think your design. – Daniel Hilgarth Oct 14 '11 at 07:12
  • @Daniel Let's see how the factory would look like with SL instead of DI: factory ctor receives container and `CreateComponent` returns: `new Component(container.Resolve(), runtimeValue)`. Each new component gets its _different_ service instance. I don't think this is "that much control over the service". Also I can't really think of a real-world example. I'm only trying to find some rules which I can then apply on any abstract model. – Suiden Oct 19 '11 at 17:43
  • @Suiden: Yes, it is a lot of control - you are controlling the lifetime of the instances of the service. – Daniel Hilgarth Oct 20 '11 at 07:58

3 Answers3

1
  1. No, it isn't. The whole purpose of a factory is to create an instance of a concrete class.
  2. Basically, yes, but as I already asked in my comment, I don't see why this is necessary. You could inject an instance of IService directly
  3. It's a bit more complicated than it needs to be. Why the double redirection IComponentProvider -> IComponentFactory? It looks like IComponentFactory doesn't add any benefit.

    Implement ComponentProvider like this:

    class ComponentProvider : IComponentProvider
    {
        ComponentProvider(IService service) { _service = service; }
    
        IComponent CreateAndCacheComponent(object runtimeValue) {
            _component = new Component(_service, runtimeValue);
            return _component;
    }
    

    This would give you the following benefits:

    1. You get rid of the unnecessary interface IComponentFactory along with the corresponding implementation.
    2. No need to register a factory for IService

Generally speaking, how you implement this it depends on what you really need:

"runtimeValue" can be the same throughout the runtime, e.g. a connection string that is read from the settings. In that case, there would be no need for a factory / provider, you could simply new up the instance and register it with the container. Everyone who needs an IComponent requests one in the constructor instead of the provider.

You would only implement a factory and pass that as a dependency around if the "runtimeValue" really changes between calls to CreateAndCacheComponent.

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • @Daniel I was afraid there'd be some confusion between `IComponentProvider` and `IComponentFactory`... The provider is meant to have other methods and have the dependency on the facory in order to create a component. The provider is the object which needs a component instance, which can only be created at runtime. – Suiden Oct 13 '11 at 20:10
  • @Daniel I'm afraid the runtimeValue really changes between calls. The component is a short-lived object, which I understood I should not register with the container. The factory however, I register it as an instance using `Container.RegisterType(new ContainerControlledLifetimeManager())` – Suiden Oct 13 '11 at 20:16
  • @Suiden: that's ok. In that case, you really need a factory. So go ahead and use it. – Daniel Hilgarth Oct 13 '11 at 20:18
1
  1. It's a big step away from Poor Man's DI, but it would be nice if you didn't have to change this factory method every time a new dependency gets added to the Component's constructor.
  2. This isn't a problem per se. Think of it like you're injecting an anonymous factory class. It can still be mocked for unit testing, and the bindings can be changed, so you're still getting the benefits of DI. But it is an added layer of abstraction which is probably not necessary. You can still avoid it in this case by injecting the IService directly into the factory, rather than a Func.
  3. Typically when using dependency injection, you want to inject services rather than values. The fact that you're finding that you have to have both may indicate that you need to reconsider your class's API. For example, maybe you should be passing the value in to the methods on the class rather than the constructor. It's hard to say what the best approach would be without knowing more details.
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • Well this code is not a real-world example, I have to admit it. But let's say, for fun, that the runtimeValue is _really_ a required dependency of the Component, meaning the component does not make sense without it. So, I find it natural that it is given on the ctor. Should I not consider it a dependency? – Suiden Oct 13 '11 at 20:22
  • 2
    @Suiden: Mixing services and values in a constructor often points to problems with the class, e.g. violation of SRP. That's why you should take a closer look. If you still consider it correct after thoroughly checking it, it is OK to mix them in the ctor. – Daniel Hilgarth Oct 13 '11 at 20:24
0

To question 1: there is nothing wrong with calling new in the factory. You have isolated instantiation to one place in your application; you just made that one place the factory instead of the container.

If you ever needed to mock or change implementations, you would just mock or change the factory implementation, rather than the Component alone.

Jay
  • 56,361
  • 10
  • 99
  • 123
  • I would be happy if I could do that on the container and totally get rid of the factory. The factory exists only because of that runtime value. Do you think there is a way to do this on the container instead? – Suiden Oct 13 '11 at 20:26