views:

761

answers:

7

I heard a lot of people saying that it is a bad practice to use IoC.Resolve(), but I never heard a good reason why (if it's all about testing than you can just mock the container, and you're done).

now the advantages of using Resolve instead of Constructor Injection is that you don't need to create classes that have 5 parameters in the constructor, and whenever you are going to create a instance of that class you're not gonna need to provide it with anything

+8  A: 

If you create classes that have 5 dependencies, you have other problems than IoC.Resolve.

Pulling dependencies (as opposed to having them pushed via constructor) completely misses the point of using IoC framework. You want to invert the dependencies. Not have your classes depend on IoC framework, but the other way around.

If you don't need all dependencies in certain scenarios, than perhaps you should split your class, or have some dependencies made optional, by making them property dependencies.


Your classes depend on the container. They won't work unless you provide them with one. Whether it's a real one, or a fake one does not matter. They are inherently bound to the container via static dependency. This imposes additional work on you to do anything with your classes. Any time you want to use your class, you need to drag the container with them. For no benefit! Service locator is just one global bag of everything, which is against probably all tenants of object oriented programming.

Krzysztof Koźmic
+1 Constructor Injection makes it painfully obvious when you are violating the SRP :)
Mark Seemann
I usually have 3 to 5 parameters, that was a way of saying it
Omu
My classes don't depend on the IoC framework because I don't use it directly, I have another wrapper class that it is calling the container
Omu
Your classes depend on the container. They won't work unless you provide them with one. Whether it's a real one, or a fake one does not matter. They are inherently bound to the container via static dependency.
Krzysztof Koźmic
@Krysztof being dependent on the container isn't necessarily evil, how often do you change IoC frameworks? Usually, never; only time I ever did was once from Unity to StructureMap. Taking it a step further and abstracting the container is more of a technically correct answer which could be applied to my answer to make it less dependent on the container.
Chris Marisic
This has nothing to do with changing container implementations. It has to do with the fact that making a call to some object (IoC.Resolve() in this case) is *not* IoC! You're asking something for your dependencies, not letting something give them to you. Used properly, you don't need to create your own "abstraction" around the container because your code simply does not know it exists!
ColinD
@ColinD I'm going to defer to @Bryan's post and agree yes it's not leveraging IoC but gives you immediate access to the service location pattern sometimes all you really need as opposed to true IoC. If you disagree please comment on my ValidationFactory example and why using IoC would be better there than service location.
Chris Marisic
@Chris I think Service Locator should be avoided as much as possible... it's a crutch that gets used in lieu of actually understanding DI and doesn't make code better the way DI does. While I agree that there are certain situations where a reference to the container is useful, I think they are few and far between and that most people asking this kind of question are not in a situation where they actually need to do that. In that case, they should be strongly recommended not to reference the container at all until they fully understand DI.
ColinD
I can concede that the service locator pattern isn't frequently needed which does coincide with my answer below that I really only have 2 instances where I've really needed to use it.
Chris Marisic
Service Locator is an anti-pattern. It's evil. If you use it, your soul is forfeit. Your dog will despise you. Global warming will increase.
Mark Seemann
@Mark Seeman Don't just say it's bad, tell us why, I heard a lot of ppl saying that it's bad a very few that tell some reasons
Omu
@Omu: A blog post is forthcoming. Watch http://blog.ploeh.dk/ for further developments :)
Mark Seemann
@Mark Seeman Sorry I didn't read it all but there is only one word "Resolve" in that big blog, could you tell us the main ideas/reasons why shouldn't we use Resolve
Omu
@Omu: It's forthcoming - meaning I haven't written that particular blog post yet, but it's in the pipeline. Rest assured that it'll be available shortly, because I personally have an interest in having a place to point people to explain why SL is an anti-pattern.
Mark Seemann
@Mark Seeman Well, it could be anti-pattern from some point of view, but it is very useful sometimes, because of it's very easy to use, and get whatever you want without the need to change the code on the other planet :D
Omu
@Omu, not really. By using SL you're taking a loan which you'll have to pay back later. You may think you're cranking code faster because of that, but in reality it's like not tying your shoes before a long run - in no time you'll find yourself face down.SL is one of these ideas that may look appealing at the beginning but in the long run they create more problems than they solve.
Krzysztof Koźmic
@Krzysztof Koźmic I'm not saying that we should use just SL, I'm using constructor injection in 95% but I have some uber generic stuff that uses SL and it's working very good
Omu
good for you...
Krzysztof Koźmic
@Mark I entirely disagree with you that service locator is an anti-pattern. It has to be applied correctly! Just like every software development pattern in existence. My answer below shows a perfect usage of the service locator pattern and if I went a step further and abstracted the ObjectFactory call (which I won't because I'll never change from StructureMap) there is absolutely nothing you can put forward as a hidden risk to my usage.
Chris Marisic
Here's why Service Locator is an anti-pattern: http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx
Mark Seemann
A: 

I would say that's a crazy amount of parameters to be injected.

Strive for one parameter, maybe 2 at most, which is in almost all scenarios possible, and of course should be an interface. Any more than that, and I smell a rat (design flaw).

Wim Hollebrandse
+10  A: 

IoC.Resolve<> is an example of the Service Locator pattern. That pattern imposes a few restrictions that constructor injection does not:

  • Objects can have no more fine-grained context than the application domain, due to the static calls
  • Objects decide which versions of dependencies to resolve. All instances of a certain class will get the same dependency configuration.
  • The temptation to couple code to the container is high, for example instead of creating an intention-revealing factory.
  • Unit testing requires container configuration, where the classes could just be created and used otherwise. (This is especially troublesome when you want to test multiple configurations of the same class, due to the second issue above.)
  • An application's structure cannot be inferred from its public API. (Constructor parameters are a good thing. They are not a problem you should feel the need to solve.)

These limitations, in my mind, relegate the Service Locator pattern to a middle ground between big-ball-of-mud and dependency injection: useful if you must use it, but by far not the best choice.

Bryan Watts
+1 for stating that Resolve is actually the service locator pattern, I never considered that before now.
Chris Marisic
All good, except for one typo. It should read: `IoC.Resolve<>` is an example of the Service Locator **anti** pattern.
Krzysztof Koźmic
what is fine-grained context and what do you mean by "which versions of dependencies to resolve" ?
Omu
Fine-grained context means that you have the same contract (interface) configured in different ways in the same application. For example, you might have an `IEmailService` with two implementations: one which sends live email, and one which sends only to a local server. If class `Foo` asks for an `IEmailService` via `IoC.Resolve<>`, it can only choose one of them - nothing about the context in which it will be used is factored into the decision of which `IEmailService` configuration to resolve. All instances of `Foo` will get the exact same configuration of `IEmailService`.
Bryan Watts
Dependency versions allow you to have multiple implementations of the same contract (interface). This is usually done with string names or something to that effect. In the example above, the live email configuration would be registered under `IEmailService` (indicating it is the default implementation of that contract). The local configuration would be registered under `IEmailService ("Local")`, indicating it must be referred to by name. With constructor injection, the object doesn't know which version it gets; with service locator, it must make the decision as to which version it gets.
Bryan Watts
so with constructor injection I can only have one version, I cannot have multiple implementations registered. Because if I have 2 classes and one requires one implementation and the second requires another implementation, how am I going to specify that using constructor injection ?
Omu
As I said before, most containers offer a way to name components. For example, a set of Autofac registrations might look like: `Register<EmailService>().As<IEmailService>();` and `Register<LocalEmailService>().As<IEmailService>().Named("Local");` The alternate implementation can then be referred to at resolution time: `Register(c => new Foo(c.Resolve<IEmailService>("Local")));` This mechanism allows you to build entire alternate object graphs, while keeping the objects themselves oblivious to their alternate configuration.
Bryan Watts
ya, I got that, I just wanted to ask whether is it possible to use the multiple implementations with constructor injections, or this is possible only with resolve ?
Omu
I'm not sure I understand your question. In the example, `Foo` is using constructor injection, because it accepts `IEmailService` in its constructor. Do you mean something different?
Bryan Watts
with resolve in you can do Constructor() { _x1 = c.Resolve<Ix>("x1"); _x2 = c.Resolve<Ix>("x2"); } but with constructor injection I don't know if this is possible Constructor(Ix x1, Ix x2) {_x1 = x1, _x2 = x2} :S _x1 and _x2 are going to be the same
Omu
I think you are asking about automatically choosing a constructor, i.e. you just give the type to the container and it picks which constructor to call. That is a different concept than constructor injection, which is why I was confused. You are correct, the container won't be able to guess which version of a dependency to resolve; you'll always get the same object. You will have to do something like this: `Register(c => new Constructor(c.Resolve<IX>("X1"), c.Resolve<IX>("X2"))`, which tells the container which constructor to call and how to call it. `Resolve` is still external to the object.
Bryan Watts
+1  A: 

I have to point out it's not necessarily evil to skip constructor injection and use static injection. There are great applications of this, the most concrete example is using it in a Factory pattern implementation.

public static class ValidationFactory
{
    public static Result Validate<T>(T obj)
    {
        try
        {
            var validator = ObjectFactory.GetInstance<IValidator<T>>();
            return validator.Validate(obj);
        }
        catch (Exception ex)
        {
            var result = ex.ToResult();
            ...
            return result;
        }
    }    
}

I use this with StructureMap to handle my validation layer.

Edit: Another example I have of using the container directly is to make some of your domain objects be singletons without making them static classes and introducing all the wierdness that static classes do.

In some of my views I wire up some entities like this. Normally I would us a Enum with a Description attribute to give me 3 value choices but the 3rd one in this case needs to be a string also and not an int so I created an interface with those 3 properties and inherit all of the domain objects from it. Then I have my container scan my assembly and register all of them automatically then to pull them out I just have

SomeObject ISomeView.GetMyObject
{
    get { return new SomeObject { EmpoweredEnumType = 
            ObjectFactory.GetNamedInstance<IEmpEnum>("TheObjectName");
        }
}
Chris Marisic
I don't think there should be a need to reference the container in this case. With this code, your classes may call the static ValidationFactory.Validate... for testing, you then have to set up the container with IValidators of the needed type(s). Instead, some object validation interface should be injected into those classes, making testing easy (can easily pass in an always-true or -false validator). A non-static version of ValidationFactory could be one implementation, which would be OK, but even then there should be other options that don't reference the container.
ColinD
I don't really see anything adding value from what you said, my factory is easily testable, yes it might require implementing ReturnFalseValidator : IValidator<someType> if I needed to mock it often I could easily wrap it into an abstract base type and use that in the configuration of my container. I personally like that all of my tests depend on my container to work as that's how it works in real life. To test your code and not have the tests actually get their dependencies from injection makes a test seem very unreliable to me.
Chris Marisic
I use resolve for automatic mapping from dtos to entities and back, I have a ValueInjecter for each type of conversion (type1 -> type2) and this ValueInjecters use Resolve to get services for getting data (all this stuff is very automatic using conventions)
Omu
A: 

you don't need to create classes that have 5 parameters in the constructor, and whenever you are going to create a instance of that class you're not gonna need to provide it with anything

A couple points:

  • If you're using a DI container, it should be creating the instances of that class for you. In that case, you don't have to provide it with anything yourself for production use. For testing, you'll have to provide it with the dependencies through the constructor, but:
  • If the class depends on (uses in some way) those 5 things you're talking about providing to the constructor (and you wouldn't be providing them if it didn't) you WILL have to provide it with them one way or the other. When testing (which is the only time you should have to call the constructor yourself) you can either pass those things to it through the constructor or you can write code to set up the container and add those 5 things to it so that when IoC.Resolve() gets called, they're actually there. Passing them to the constructor is a lot easier, I'd say.

Dependencies will exist even if you don't make that apparent through the class's API (it's constructor in this case). However, it'll be a lot harder to understand and test classes that try to hide their dependencies like this.

ColinD
it's my controller classes (asp.net mvc), they have from 3 to 7 parameters sometimes (every parameter is an interface that does his thing, it's the srp stuff)
Omu
+3  A: 

Ioc.Resolve is essentially the Service Locator pattern. It has its place, but is not ideal. Constructor injection is preferred from an architecture standpoint, as dependencies are more explicit, whereas the SL hides the dependencies within a class. This reduces testability, and makes the process more complex than it needs to be.

If I may, I would suggest you read my recent series on techniques to reduce code coupling, which covers SL, DI, and IoC.

Grant Palin
that's a good answer, it explains a little bit, but in some cases when doing some super static and generic things the Service Locator is kinda useful
Omu
+2  A: 

One advantage is that with constructor injection, all class dependencies are visible up-front.

With .Resolve you have to read the code just to figure the dependencies out.

orip
they are all visible with constructor injection, but you don't need to know them with Resolve (only when testing)
Omu
Actually, you need to figure them out when changing or refactoring the application, and this is when IoC really shines.
Nicholas Blumhardt