Closed DanVanAtta closed 9 years ago
I've used spring, guice, and CDI in the past though they are all pretty full-featured and heavy. Dagger looks pretty simple and lightweight. I like the idea though I think it might be better to hold off on this for now. While DI has many benefits, it can also make the code harder to read and refactor. I also don't want to make too many changes all at once and a good portion of the code needs refactored to even take advantage of DI.
DI is a technique to break up components, which allows for testing and further refactor. I wouldn't shut the door on it since in some cases it may be necessary to allow us to refactor. But I'm not recommending we go full throttle and require DI be done everywhere
More the point is to ensure that there is agreement on a DI framework so we all don't all start adding in five different ones.
I think this falls into the same category as continuous builds in my mind as in its just not that high of a priority at this point based on what we would gain from it. If we were starting a green field project then I would definitely pick a DI framework to start as you immediately get benefits and can make sure its consistent across the code base.
Adding a DI framework into a sizeable code base that needs a lot of refactoring is much more difficult to get benefits and can end up leading to increased complexity initially. My concern is that we will implement it in only 1% of the code base and then it just ends up making that portion of the code base less consistent and harder to maintain.
I'm fine if we want to discuss which one we'd like to implement but would prefer to focus on a few things at a time and not make 100 different changes to a small percentage of the code base but rather prioritize the most important things and make sure to drive them to completion. In Agile, sort of the idea of swarming on the most important stories rather than adding lots of stories and none of them getting completed.
Okay, I think I see maybe where the priority disagreement is. Given the lack of DI in the code, having reviewed and tried to test some of it, I'm of the opinion that large stretches are not feasibly testable without some refactor, some of that being DI. Said in another way, to successfully refactor and test, we have to use DI in some locations (in reality we could spend hours and hours trying to test code that is pretty untestable, but it's more efficient often to carve off chunks methodically and do a combination of refactor and test). Said yet another way, DI can be an important way to make untestable code testable.
So given our shared view that test is a priority, I believe DI is also a priority because it'll help us test a lot of un-testable code in the system. We can always do DI by hand, the old fashioned way and create a lot of Factory objects, but using a DI system from get go will get us moving faster I think.
I've really only ever heard of systems like Spring being used in the context of large enterprise web systems, and web frameworks. What does this buy us, and can you give me a simple example of it being used?
Spring does a lot of things. Dependency injection containers are useful for small projects as well, Spring has evolved over time to fill that role.
Dependency injection is a good design pattern to limit unnecessary coupling between classes. This improves class simplicity and often allows for easier testing.
Consider this code:
public class foo { private boolean userPreferenceFlag; public class foo() { userPreferenceFlag = UserPreferences.getInstance().getPreference(); } }
This code is very hard test. If you do test it, then the test will be brittle since the scope of changes that would impact that test span both this class and also the getInstance() singleton guy. To boot, mocking or replacing that singleton might be impossible, so we could be stuck with having the singleton.
Dependency injection is also the "don't call us, we'll call you" principle.
You could change the code in this way to inject the dependency:
public class foo { private boolean userPreferenceFlag; public class foo( UserPreferenceParser userPreferences ) { userPreferenceFlag = userPreferences.getPreference(); } }
With the dependency injected this code has some improvements:
I could go on a bit more with some extra benefits, but DI gives you easier test and cleaner interfaces.
So, DI is a good thing, it really helps.
To get DI, you wind up with a lot of factory objects to create instances. Frameworks like Spring and Dagger automate this factory object stuff, so you get a more robust glue, less code to manage, and less coupling. The reduced coupling makes things easier to modify and test. Hence, it buys is quite a lot.
To use dagger, we simply drop in the one jar, then we use "@Inject" and "@Provides" annotations. That is the 90% use-case, there is some additional complexity, but using the framework is simple, it improves our capability to do DI, and DI is good for improving code design to reduce complexity and allow testing.
I get (and previously knew of, and agreed with) the idea behind dependency injections for simple examples like your foo class example.
What I don't get are the dependency injection frameworks. Could you give more example of that, for dagger?
I don't know dagger so well, but on the spectrum of DI frameworks, it looks like it is the least weight and was recommended by the other tripleA developers.
In this example, It would be a matter of adding @Injected and @Provides annotations, so something like:
public class foo { private boolean userPreferenceFlag; @Injected public class foo( UserPreferenceParser userPreferences ) { userPreferenceFlag = userPreferences.getPreference(); } }
@Provides public class UserPreferenceParser { }
Dagger is smart enough to create the objects for you, gives compile time checking, no XML configurations. The compile time checking is really attractive, means you can put an @Injected tag in the wrong spot and not have something get injected. Looks like it can get slightly more complex, the dagger docs has handling those cases as perhaps not too bad: http://google.github.io/dagger/
two cents more, spring has similar annotations these days, "@Component" and "@Autowired", but those are not checked at compile time AFAIK.
And then what happens after you add those annotations?
Dagger creates a dependency graph at compile time and ensures that it can be satisfied at runtime. @Injected indicates a dependency, @Provides indicates something that can satisfy dependencies. There is additional syntax in the case when type alone is not enough to figure out a dependency. DI also has the framework calling constructors for you, so the annotations would have classes get created and wired together automatically for you.
Ok, but how do you use these annotations? You said we do not need factory classes, so what would it look like instead? I feel like I am missing the final piece here, even if it is obvious to everyone else.....
Basically the instance that needs an objects get the @Injected and the instance that gives the object gives the @Provides. Looks like basically dagger will create and wire the classes together for you. So if you have a method marked as @Provides and fits with an @Injected labelled over a constructor, my understanding is that dagger would call the constructor for you, and it would call the method first too and take the value from that and pass it to the constructor. So basically whenever you are using a value that is marked with @Injected, you're basically going through an auto-magic factory to get access to that value/object.
I'll need to play around with Dagger more to give a better description, I only have an overview of it right now. It does look like it fits the bill for what we want. Only thing remaining in this question is if anyone has objections of using this framework over others.
I've been thinking a dependency injection container would be very helpful. Particularly given all the singletons we have floating around. In triplea-test it was recommended to use Dagger2. So far it looks nicer for us than Spring by being far lighter in weight. I pulled down a Jar file and threw in some annotations, so far so good.
Question then, is everyone good with us using Dagger2 for dependency injection?