Shorter version of the rest of the question: I've got a problem, and an IoC container is a tool. The problem sounds like something IoC might be able to solve, but I haven't read enough of the tool's instructions to know for sure. I'm curious if I picked the wrong tool or which chapter of the instruction manual I might be able to skip to for help.
I work on a library of Windows Forms controls. In the past year, I've stumbled into unit testing and become passionate about improving the quality of our automated tests. Testing controls is difficult, and there's not much information about it online. One of the nasty things is the separation of interaction logic from the UI glue that calls it leads to each control having several more dependencies than I would normally consider healthy for a class. Creating fakes for these elements when testing their integration with the control is quite tedious, and I'm looking into IoC for a solution.
There's one hurdle I'm not sure how to overcome. To set up the container, you need to have some bootstrapper code that runs before the rest of the application. In an application there is a very clear place for this stuff. In a library it's not so clear.
The first solution that comes to mind is creating a class that provides a static instance of the container and sets up the container in its type initializer. This would work for runtime, but in the test environment I'm not sure how well it would work. Tests are allowed to run in parallel and many tests will require different dependencies, so the static shared state will be a nightmare. This leads me to believe the container creation should be an instance method, but then I have a chicken and egg problem as a control would have to construct its container before it can create itself. The control's type initializer comes to mind, but my tests won't be able to modify this behavior. This led me to think of making the container itself a dependency of the control where user-visible constructors provide the runtime default implementation, but this leaves it up to my tests to set up their own containers. I haven't given this much thought, but it seems like this would be on the same level of effort as what I have now: tests that have to initialize 3-5 dependencies per test.
Normally I'd try a lot of things on my own to see what hurts. I'm under some harsh deadlines at the moment so I don't have much time to experiment as I write code; I only get brief moments to think about this and haven't put much to paper. I'm sure someone else has had a similar problem, so it'd be nice if I didn't have to reinvent the wheel.
Has anyone else attacked this problem? Are there some examples of strategies that will address these needs? Am I just a newbie and overcomplicating things due to my inexperience? If it's the latter, I'd love any resources you want to share for solving my ignorance.
Update:
I'd like to respond to Mark Seeman's answer, but it will require more characters than the comment field allows.
I'm already toying with presentation model patterns. The view in this case is the public control class and each has one or more controller classes. When some UI event is triggered on the control, the only logic it performs is deciding which controller methods need to be called.
The short expression of an exploration of this design is my controller classes are tightly coupled to their views. Based on the statement that DI containers work with loosely coupled code I'm reading "wrong tool for the job". I might be able to design a more loosely coupled architecture, at which point a DI container may be easier to use. But that's going to require some significant effort and it'd be an overhaul of shipped code; I'll have to experiment with new stuff before tiptoeing around the older stuff. That's a question for another day.
Why do I even want to inject strongly coupled types rather than using local defaults? Some of the seams are intended to be extensibility points for advanced users. I have to test various scenarios involving incorrect implementations and also verify I meet my contracts; mock objects are a great fit.
For the current design, Chris Ballard's suggestion of "poor man's DI" is what I've more or less been following and for my strongly coupled types it's just a lot of tedious setup. I had this vision that I'd be able to push all of that tedium into some DI container setup method, but the more I try to justify that approach the more convinced I become that I'm trying to hang pictures with a sledgehammer.
I'll wait 24 hours or so to see if discussion progresses further before accepting.
Dependency Injection (Inversion of Control) is a set of principles and patterns that you can use to compose loosely coupled code. It's a prerequisite that the code is loosely coupled. A DI Container isn't going to make your code loosely coupled.
You'll need to find a way to decouple your UI rendering from UI logic. There are lots of Presentation Patterns that describe how to do that: Model View Controller, Model View Presenter, Presentation Model, etc.
Once you have good decoupling, DI (and containers) can be used to compose collaborators.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With