I understand your confusion and it took me some time to understand how these concepts were related together. So here is my (somehow personal) explanation of all this:
**1. Inversion of Control**
[Inversion of control][1] is a design principle rather generic that refers to the decoupling of the specification of a behavior from when it is actually executed. Compare for instance,
myDependency.doThis();
with
myDependency.onEventX += doThis();
In the latter, there is *no direct invocation* which is more flexible. In its general form, inversion of control relates to the *observer pattern*, *events*, or *callbacks*.
**2. Dependency inversion**
Dependency inversion is another design principle. Roughly speaking, it says that higher-level abstraction should not depend directly on lower-level abstractions; this results indeed in a design where higher-level abstraction can not be reused without the lower-level abstractions.
class MyHighLevelClass {
MyLowLevelClass dep = new MyLowLeverClass();
}
class App {
void main() { new HighLevelClass().doStuff(); }
}
Here, `MyHighLevelClass` can not compile without access to `MyLowLevelClass`. To break this coupling, we need to *abstract* the low level class with an interface, and remove the direct instantiation.
class MyLowLevelClass implements MyUsefulAbstraction { ... }
class MyHighLevelClass {
MyUsefulAbstraction dep;
MyHighLevelClass( MyUsefulAbstraction dep ) {
this.dep = dep;
}
}
class App {
void main() { new HighLevelClass( new LowLevelClass() ).doStuff(); }
}
Note that you don't need anything special like a container to enforce dependency inversion, which is a principle. A good reading is [The Dependency Inversion Principle][2] by Uncle Bob.
**3. Dependency injection**
Now comes dependency injection. To me `dependency injection = IoC + dependency inversion`:
1. dependencies are provided externally so we enforce the dependency inversion principle
2. the container sets the dependencies (not us) so we speak of inversion of control
In the example I provided above, dependency injection can be done if a container is used to instantiate objects and automatically *inject* the dependency in the constructor (we speak then frequently of DI container):
class App {
void main() { DI.getHighLevelObject().doStuff(); }
}
Note that there are various [form of injections][3]. Note also that under this perspective, [setter injection][4] can be seen as a form of callback -- the DI container creates the object then calls back the setter. The flow of control is effectively inverted.
**4. AOP**
Strictly speaking, AOP has little to do with the 3 previous points. The [seminal paper on AOP][5] is very generic and present the idea of weaving various sources together (possibly expressed with different languages) to produce a working software.
I won't expand more on AOP. What is important here, is that dependency injection and AOP do effectively plays nicely together because it makes the weaving very easy. If an IoC container and dependency injection is used to abstract away the instantiation of objects, the IoC container can easily be used to weave the aspects before injecting the dependencies. This would otherwise requires a special compilation or a special `ClassLoader`.
Hope this helps.
[1]:
[To see links please register here]
[2]:
[To see links please register here]
[3]:
[To see links please register here]
[4]:
[To see links please register here]
[5]: