In episode #362 of .NET Rocks! (www.dotnetrocks.com), Carl and Richard talked to James Kovacs, who gave an incredibly clear definition of dependency inversion, dependency injection, and inversion of control.
James Kovacs is an independent architect, developer, trainer, and jack-of-all-trades, specializing in agile development using the .NET Framework. He is passionate about helping developers create flexible software using test-driven development (TDD), unit testing, object-relational mapping, dependency injection, refactoring, continuous integration, and related techniques. He is a founding member of the Plumbers @ Work podcast, which is syndicated by MSDN Canada Community Radio. He has published articles in MSDN Magazine, most recently “Loosen Up: Tame Your Software Dependencies for More Flexible Apps” in the March 2008 issue. James is a Microsoft Most Valuable Professional (MVP) - C# Architecture and card-carrying member of ALT.NET, a group of software professionals continually looking for more effective ways to develop applications. He received his Masters degree from Harvard University.
Richard Campbell: I think that maybe we really need to lay down more of a foundation of core concepts around this just to sort of set the stage for what Windsor is all about. I know you’re a Windsor fan too, James, so I want to start with inversion of control but I hope we can get to talk about where Castle plays in the whole equation.
Carl Franklin: Yes, let’s talk about the fundamentals first.
James Kovacs: Traditionally we’ve had very tightly coupled software architectures where you have one concrete component talking to another. So you’ve got your service component, your customer service, that talks directly to some data access component and the problem with that is it’s very difficult for those two components to vary independently, so if you want to switch to a different database platform or you want to add in logging or auditing or anything else, you really have to tear everything apart and put it all back together.
Carl Franklin: Right.
James Kovacs: So, one of the core concepts that came about was the dependency inversion principle which was popularized by Robert C. Martin, and he states that high level modules should not depend on the low level ones, but they should both depend on abstractions. So, in more concrete terms, rather than having your customer service component depend on your data access component, both of them should depend on some interface or abstract-based class like an ICustomer repository where the repository is your data access component. So, what you’ve done there is you’ve essentially decoupled those two components and all the customer service component knows about is that it’s talking to some IRepository, some IComponent. So, you are actually free at runtime to switch that for something else and this introduces a lot of flexibility into your software.
Carl Franklin: Yup, and that’s really what it’s all about. As changes come up, you just want to make sure that you’re ultimately flexible. I think a big mistake that a lot of developers make is trying to predict the future in the architecture session, trying to come up with all the different scenarios that could happen and therefore try to architect for them right upfront whereas this approach is let’s just architect for flexibility so that when something unknown and unseen comes along, we can adapt quickly.
James Kovacs: You’re absolutely right, Carl. That’s exactly what we were aiming for. It’s all about focusing on highly cohesive architectures and loose coupling between components, just things that we’ve talked about for years and this is a way to get to it.
Carl Franklin: So, that’s dependency inversion. Tell us what dependency injection is.
James Kovacs: What dependency injection is, now you have a true dependency inversion, you have a high level component depending on an interface. What you want to be able to do is substitute that at runtime. You can use dependency injection to supply components dependencies through its constructor, typically through its constructor. What you do is you’d say, “New customer service” and one of the first dependencies would be new customer repository.
Carl Franklin: Right and the dependency is just any component that is accessed during the lifespan of that object.
James Kovacs: That’s right, so it’s just some other component that is used by the piece of software that you’re currently working with, in this case the service.
Carl Franklin: So, is it as simple as just supplying those dependencies on the constructor?
James Kovacs: That’s exactly it. The other option that a lot of people use is service locator. What service locator is, in your customer service, in your constructor, you will walk up to a factory or something and say, “Hey, create me this dependency of mine.”
Carl Franklin: Right.
James Kovacs: That’s definitely another way to do it. The problem with that is you don’t get very good testability because all your dependencies are locked away inside of this service, which makes actually testing the service in isolation of the database and everything else very difficult. It’s possible but quite difficult. By having all of your dependencies supplied into your constructor in your test, you just supply fake diversions of them. You can use the mocking framework or you can write them yourself and just have some class that implements that interface and supply a default behavior but that doesn’t actually touch databases. It doesn’t do logging and auditing and things like that. What it allows you to do is unit test components in isolation.
Carl Franklin: Yes.
James Kovacs: Now, one of the nice things about this is if software is only used in one place, it typically is fairly inflexible because it’s only suited for one purpose. As soon as you have to design it to be used in two different places, it becomes much more flexible. The two different places in this case are tests and production code. So, you all of a sudden are gaining a lot of flexibility in your design by thinking of it, “Okay, how am I going to test this?” So, by supplying all of your dependencies in your constructor, there you have a way of easily testing because you can supply mocks or stubs or your own handcrafted fake objects at test time, but then have real versions in production code.
Carl Franklin: And the other concept, of course, is inversion of control and inversion of control containers. We’ve talked about this a little bit before on the show but I have a feeling that it was a little bit unclear. I really appreciate the clarity of your brief and very succinct definitions here. So, let’s define inversion of control.
James Kovacs: You saw that dependency injection is a good thing. Now, you can imagine you’ve got a chain of dependencies, you’ve got some UI component that needs to build a service component that needs to get access to a data access component and each of these things in turn have their own dependencies. So, as you go up the chain, all of a sudden you want to create the repository so you need to supply dependencies. You need to create the service so you need to supply the repository and the other dependencies. So, by the time you hit the UI, the UI has to know all about data access components, service components, logging components, auditing components, and that’s the wrong place to be assembling your architecture…
Carl Franklin: That is the wrong place.
James Kovacs: In your UI. That’s what you’re trying to get away from…
Carl Franklin: I see.
James Kovacs: Is having all kinds of logic inside your UI controls.
Carl Franklin: Sure.
James Kovacs: So, the way that you get away from this is you use an inversion of control container and inversion of control container is nothing more than a factory for components.
Carl Franklin: Because you have to supply all of the dependencies, you want to start from another container that can create those dependencies and then pass them in so the UI is actually the last thing in the chain.
James Kovacs: That’s exactly what it is. It sounds really complicated but that is what it is. What you do is you walk up to the container and you say, “Hey, I need an ICustomer service,” and it looks in its setup and says, “Oh, an ICustomer service is this customer service class. This customer service class needs ICustomer repository,” which is the SQL customer repository. It also needs an auditing component and it figures out what all the dependencies are on the chain, constructs them in reverse order, and then supplies you with a fully constructed object at runtime. So, it’s just a factory for components.
Carl Franklin: Yeah, that makes total sense.
James Kovacs: You’re basically constructing a component at runtime. Inversion of control containers provide you with a lot of services over and above object construction but at the fundamental level, that’s it, and a lot of people say, “I can’t bring in a third-party component into my application. I can’t bring something as big as Castle Windsor or StructureMap,” or something like that. The reality is if you’re in a situation where you can’t take one of the dependencies, you can build your own inversion of control container in about two dozen lines of code and I actually walked through that in my articles, so [your listeners] can just go read up in the “Loosen Up” article.
Richard Campbell: That’s a great idea, James, because most people push back on this as too complex.
Carl Franklin: Right.
James Kovacs: Honestly, Richard, it’s two dozen lines of code. Fundamentally, what do you have? You have a hash table. The hash table has a bunch of interfaces versus concrete types. So, in a very simple container that you construct yourself, I call it the do-it-yourself container; all you do is when your application starts up in your sub main or your public static main method if you’re doing C#, you just construct all the types, construct all the dependencies, build everything, throw them into this hash table and then at runtime, you ask for, “Hey, I need an ICustomer service,” and it looks it up in the hash table and passes it back to you. Full-fledged containers can do much more than that and will walk dependency chains for you but 90% of what they supply, you can write yourself and it’s really quite straightforward.
Richard Campbell: And very quickly.
James Kovacs: And if you understand that, you then can buy into and understand what these other containers are and actually be in a position to evaluate, “Okay, should I go with Spring.Net? Should I go with StructureMap? Should I go with Windsor? Should I go with Unity? Which inversion of control container is right for me?”
Carl Franklin: I just heard some heads spinning out there because you just threw out a whole bunch of products that nobody really knows what they do. What are we talking about when we’re looking at these frameworks?
James Kovacs: They are all inversion of control containers. Spring.Net is a port of the Spring Framework from Java, a very standard Java architecture is Spring for inversion of control NHibernate for object relational mapping. You can do the same thing on the .NET side of things.
Richard Campbell: Right and we had Mark Pollack on last year talking about Spring.Net.
James Kovacs: Spring is one container, Castle Windsor is another one, part of the Castle project and it’s my personal favorite. The main advantage of Windsor from what I’ve seen over Spring is Windsor has a lot more flexibility in configuration. Spring tends to be a lot more verbose, a lot of XML setup. That might have changed in the last year or so since last I looked at it, but it historically has been rife with XML. You just have to dig XML files and configure all your types and how they all hook together. With Windsor, you can do that but you get a lot more options in terms of configuration such as configuring it through a scripting language, configuring it in code. Craig Neuwirt created a fluent interface which is now part of the latest build of castle so you can actually string things together. The fluent interface idea actually came from Jeremy Miller and StructureMap so with StructureMap, Jeremy’s whole point was I can actually rather than going into XML and configuring it in XML, I can actually go ahead and build it up saying “Object factory, for this interface, this is the concrete implementation,” and so you end up using generics to define what your component structures are. So, each of them has their own inversion of control containers. They are often part of a larger piece. Windsor is part of the Castle project, so Windsor has got very good hooks into Castle Mono Rail and Castle Active Record and the other projects that’s part of the Castle umbrella. Spring has its own pieces for building out applications. So, it depends on the style of development you want to do. If you want a really lightweight framework, StructureMap is a good one. Jeremy has got some really good ideas in that and then Unity is Microsoft’s play into this space. People have probably heard of Object Builder. Object Builder is an object construction framework but it doesn’t supply inversion of control. Unity builds on top of Object Builder and it is a full-fledged inversion of control container.
Richard Campbell: Interesting. This is a CodePlex project from the pattern & practices team, right?
James Kovacs: Yes. Chris Tavares of the pattern & practices group was the lead on it, a very great guy. It’s got a bit of a different flavor to it than Windsor does but they all fundamentally do the same thing-figure out your dependency structure at runtime, which allows you to be very flexible. The nice thing about it is, let’s say I’ve got some sort of data access component and I want to find out every time the databases touched. Well, I could go in there and start hardwiring in some logging components or the other thing I can do is I can create a brand new component called a logging data access component. It internally has the real data access component, just another IData access. With your inversion of control container, you just wrap it. You use the decorator pattern when you call into the IData access component, you actually get an auditing data or a logging data access component, which does the logging and then that delegates through to the actual data access component so you can actually implement logging without actually modifying the original data access component which dramatically reduces the chances of introducing a bug into your system because you’re not editing the original code. It’s the whole idea that the open-closed principle that classes should be open for extension, but closed for modification. You don’t want to open up a code file but you want to be able to extend it either by deriving a class or using composition to introduce new functionality, new behavior into the system while not modifying existing working behavior.
Carl Franklin: This is what AOP, aspect-oriented programming, is really all about, isn’t it? You want to keep sort of the goo where you can put these things that happen in a separate place in your code, staying out of your business logic code.
James Kovacs: That’s right. Aspect-oriented programming is a little bit different flavor. It’s a bit of orthogonality, you have orthogonal concerns. Logging is different from data access.
Carl Franklin: Let’s define that big word, shall we?
James Kovacs: Orthogonality or something being orthogonal just means at a right angle. So, they’re completely separate concerns.
Carl Franklin: At right angles?
James Kovacs: Right-angled, so doing something on one access doesn’t affect the other access. If you got an XY coordinates, you can move up and down your Y axis without affecting what your X coordinate is.
Carl Franklin: So you’re just talking totally decoupled. That’s what that means?
James Kovacs: You’re just decoupling your code. So, the idea of aspect-oriented programming is to be able to implement an auditing component and rather than having to implement a customer auditing component and an orders auditing component and a foo auditing and a bar auditing component-all these different components-you implement one auditing component that receives all calls and it can make decisions. It’s a very different style of programming and there are aspect-oriented programming frameworks. Inversion of control containers are often an ingredient in getting there; you don’t need to use AOP when you’re doing inversion of control though. They are two separate but related concepts.
Richard Campbell: One of the frustrations I have with this technology, James, is that every example I see is about logging. Can we come up with a story that’s more complicated, like how I could fix something really challenging inside of an app using this technique?
James Kovacs: Actually, I used this in one of my customer’s applications. They needed to have validations rules. So, my service component needed a bunch of validation rules for what a valid purchase order was.
Richard Campbell: Okay.
James Kovacs: Traditionally, you have this big, ugly if-then-else switch block, and every time you need to add in a new rule, you add something else into this big gory validate method.
Richard Campbell: Which is also a very test-resistant chunk of code, like it takes a lot of tests to deal with that huge switch block.
James Kovacs: Exactly, and the interactions between the different branches of the ifs and switches, it just gets unwieldy very quickly.
Richard Campbell: Right.
James Kovacs: What I did instead is my service component depends on validation components. The validation components get injected at runtime. So, in my configuration, I just have a list of all my validation components and they are very simple. They are: does the purchase order have a date? Is the purchase order over a certain amount? Is the purchase order to a particular customer? It has very simple rules about what a valid purchase order looks like. Those can be tested independently and very easily. The service then just reiterates through whatever collection is given.
Richard Campbell: And it’s probably using some kind of data structure to figure out what rules I need to apply to what data structures.
James Kovacs: That’s right. So, it’s very simple to add in a new rule, I just create a new IValidation rule, configure it in my inversion of control container, and next time the application comes up, it’s got it. I don’t need to go through a lot of contortions to test it and to make sure all the various branches of logic are working. It’s a very simple matter of just adding this new validation rule. I’m not going to break the existing ones. If for some reason I do discover a problem within an existing validation rule or I could use it for promotions, so I’ve got a promotion that’s only good for a certain amount of time, I need to disable it, turn it off. It’s a matter of just commenting out a line of code. You can do this in an XML configuration file. You can do it in a separate assembly that configures the application. There are a variety of ways of doing it, but it’s very easy to dynamically add and remove pieces to your application without affecting the overall structure.
Richard Campbell: I was about to come at you and say, “Should I just be using a rules engine for this?” and I’ve answered my own question just thinking about what you’re saying here which is that the challenge I have with the rules engine is while I avoid writing code, I’m limited to the creativity of the rules engine author and you’re giving me a model here where I can still get to write code but without the punishment of the big switch where it’s a bear to test and it’s likely I can break things. Here, I still get to write the code the way I want to write it, but I’m injecting it into the system in a way that it doesn’t endanger the existing system.
James Kovacs: That’s exactly right, Richard, and sometimes a rules engine is the right answer but sometimes it is not. It’s a matter of looking at the application you’re trying to create-the intended end-users. Oftentimes, it’s programmers who are the ones writing the rules and testing it. There, you’re probably going to be much more comfortable working in C#, writing unit tests around it. If you do need to hand it off to an end business user to configure and write the rules, in that case, then commercial rules engines do have front-end editors and all the other pieces that go along with it. You have to look at the application you’re writing and the end-user base and figure out what is going to be best for your client, but the point is that you don’t need whip out a big rules engine just because you’ve got a few validation rules or you need to make your system configurable.
Richard Campbell: Yeah, I like the fact that I don’t have to take things apart in this respect. One of the other concerns I have about this is this must make code reading more challenging like bringing somebody new into a project. You’ve got to really understand the flow of your program. It’s not easy now.
James Kovacs: Yes and no. It’s not as easy because it’s not as linear and people might not have seen it before. As soon as you have worked with this out of system though, it becomes second nature. You see a block of code like in your service component, it will say, “For each validation rule in the validation rules collection that was injected, rule out validate.” So, that’s easy enough to follow along. Your next step is just to find out everybody who implements the IValidation rule interface and if you’re working with a tool like most people who are doing test-driven development, use ReSharper. It’s a single keystroke to say find me all implementers of this interface and there are other tools that will do this. That’s one of the reasons that people don’t kind of understand the value of a tool like ReSharper until they start working in these flexible systems and the ability to navigate through your code base easily and find all implementing types, go to derived types, navigate inheritance hierarchies very quickly, you don’t kind of see the value in it.
Richard Campbell: You know what? You’ve switched tools app. When we first started this conversation, you said, “Okay, I want to insert this capability in all these classes.” My first thought was, “Okay, I’m going to make friends with the Find and I’m searching code.” Now, you’re talking more about doing that same task but doing it from an object browsing point of view.
James Kovacs: Yes. So, on the one hand, you need to have things happen at runtime, you need to have actual code constructed and object supplied. On the other hand, as a programmer, you need to be able to efficiently navigate your code base and figure out what the heck is going on. What are all the possible rules that could be injected here? What are the implementers of the interfaces? Who override these methods? In that case, a tool like ReSharper is invaluable because it can answer those questions very quickly and very efficiently with just a few keystrokes.
The conversation continues online at shrinkster.com/11RN.