Skip to main content
edited body
Source Link
Ben Aaronson
  • 7k
  • 1
  • 31
  • 30
  1. Changing how your classes work together is not a violation of OCP. If it was, then all dependency injectioninversion would be encouraging a violation of OCP. If this was the case, they wouldn't both be in the same SOLID acronym!
  1. Changing how your classes work together is not a violation of OCP. If it was, then all dependency injection would be encouraging a violation of OCP. If this was the case, they wouldn't both be in the same SOLID acronym!
  1. Changing how your classes work together is not a violation of OCP. If it was, then all dependency inversion would be encouraging a violation of OCP. If this was the case, they wouldn't both be in the same SOLID acronym!
added 2789 characters in body
Source Link
Ben Aaronson
  • 7k
  • 1
  • 31
  • 30

##Addendum##

I just noticed your intro paragraphs could use some addressing too. You sardonically say that IoC containers "aren’t employing some secret technique we’ve never heard of" to avoid messy, duplication-prone code to build a dependency graph. And you're quite right, what they're doing is actually addressing these things with the same basic techniques as we programmers always do.

Let me talk you through a hypothetical scenario. You, as a programmer, put together a large application, and at the entry point, where you're constructing your object graph, you notice you have quite messy code. There are quite a few classes that are used again and again, and every time you build one of those you have to build the whole chain of dependencies under them again. Plus you find you don't have any expressive way of declaring or controlling the lifecycle of dependencies, except with custom code for each one. Your code is unstructured and full of repetition. This is the messiness you talk about in your intro paragraph.

So first, you start to refactor a bit- where some repeated code is structured enough you pull it out into helper methods, and so on. But then you start to think- is this a problem that I could perhaps tackle in a general sense, one that isn't specific to this particular project but could help you in all your future projects?

So you sit down, and think about it, and decide that there should be a class that can resolve dependencies. And you sketch out what public methods it would need:

void Bind(Type interfaceType, Type concreteType, bool singleton);
T Resolve<T>();

Bind says "where you see a constructor argument of type interfaceType, pass in an instance of concreteType". The additional singleton parameter says whether to use the same instance of concreteType each time, or always make a new one.

Resolve will simply try to construct T with any constructor it can find whose arguments are all of types which have previously been bound. It can also call itself recursively to resolve the dependencies all the way down. If it can't resolve an instance because not everything has been bound, it throws an exception.

You can try implementing this yourself, and you'll find you need a bit of reflection, and some caching for the bindings where singleton is true, but certainly nothing drastic or horrifying. And once you're done- voila, you have the core of your very own IoC container! Is it really that scary? The only real difference between this and Ninject or StructureMap or Castle Windsor or whatever one you prefer is that those have a lot more functionality to cover the (many!) use cases where this basic version wouldn't be sufficient. But at its heart, what you have there is the essence of an IoC container.

##Addendum##

I just noticed your intro paragraphs could use some addressing too. You sardonically say that IoC containers "aren’t employing some secret technique we’ve never heard of" to avoid messy, duplication-prone code to build a dependency graph. And you're quite right, what they're doing is actually addressing these things with the same basic techniques as we programmers always do.

Let me talk you through a hypothetical scenario. You, as a programmer, put together a large application, and at the entry point, where you're constructing your object graph, you notice you have quite messy code. There are quite a few classes that are used again and again, and every time you build one of those you have to build the whole chain of dependencies under them again. Plus you find you don't have any expressive way of declaring or controlling the lifecycle of dependencies, except with custom code for each one. Your code is unstructured and full of repetition. This is the messiness you talk about in your intro paragraph.

So first, you start to refactor a bit- where some repeated code is structured enough you pull it out into helper methods, and so on. But then you start to think- is this a problem that I could perhaps tackle in a general sense, one that isn't specific to this particular project but could help you in all your future projects?

So you sit down, and think about it, and decide that there should be a class that can resolve dependencies. And you sketch out what public methods it would need:

void Bind(Type interfaceType, Type concreteType, bool singleton);
T Resolve<T>();

Bind says "where you see a constructor argument of type interfaceType, pass in an instance of concreteType". The additional singleton parameter says whether to use the same instance of concreteType each time, or always make a new one.

Resolve will simply try to construct T with any constructor it can find whose arguments are all of types which have previously been bound. It can also call itself recursively to resolve the dependencies all the way down. If it can't resolve an instance because not everything has been bound, it throws an exception.

You can try implementing this yourself, and you'll find you need a bit of reflection, and some caching for the bindings where singleton is true, but certainly nothing drastic or horrifying. And once you're done- voila, you have the core of your very own IoC container! Is it really that scary? The only real difference between this and Ninject or StructureMap or Castle Windsor or whatever one you prefer is that those have a lot more functionality to cover the (many!) use cases where this basic version wouldn't be sufficient. But at its heart, what you have there is the essence of an IoC container.

typo corrected
Source Link
gnat
  • 20.8k
  • 29
  • 115
  • 294

These libraries are absolutely not "transparent" in how they work, or what they're doing behind-the-scenes. But that's not a detriment, because they support good programming principles in their consumers. So whenever you talk about a library like this, remember that as a consumer of a library, it's not your job to worry about its implementation or maintainancemaintenance. You should only worry about how it helps or hinders the code you write which uses the library. Don't conflate these concepts!

These libraries are absolutely not "transparent" in how they work, or what they're doing behind-the-scenes. But that's not a detriment, because they support good programming principles in their consumers. So whenever you talk about a library like this, remember that as a consumer of a library, it's not your job to worry about its implementation or maintainance. You should only worry about how it helps or hinders the code you write which uses the library. Don't conflate these concepts!

These libraries are absolutely not "transparent" in how they work, or what they're doing behind-the-scenes. But that's not a detriment, because they support good programming principles in their consumers. So whenever you talk about a library like this, remember that as a consumer of a library, it's not your job to worry about its implementation or maintenance. You should only worry about how it helps or hinders the code you write which uses the library. Don't conflate these concepts!

added 65 characters in body
Source Link
Ben Aaronson
  • 7k
  • 1
  • 31
  • 30
Loading
added 1 character in body
Source Link
Ben Aaronson
  • 7k
  • 1
  • 31
  • 30
Loading
Source Link
Ben Aaronson
  • 7k
  • 1
  • 31
  • 30
Loading