Here is my (somehow personal) explanation of all this:
1. Inversion of Control
Inversion of control 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 by Uncle Bob.
3. Dependency injection
Now comes dependency injection. To me dependency injection = IoC + dependency inversion
:
- dependencies are provided externally so we enforce the dependency inversion principle
- 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. Note also that under this perspective, setter injectioncan 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 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
.
这是我(以某种方式个人)对所有这些的解释:
1.控制反转
控制反转是一种相当通用的设计原则,它指的是行为规范与实际执行时的解耦。比较一下,
myDependency.doThis();
同
myDependency.onEventX += doThis();
在后者中,没有更灵活的直接调用。在其一般形式中,控制的反转涉及观察者模式,事件或回调。
2.依赖注入
依赖注入是另一种设计原则。粗略地说,这意味着更高级别的抽象不应该直接依赖于较低级别的抽象; 这种结果确实存在于一种设计中,如果没有较低级别的抽象,则无法重用更高级别的抽象。(大概意思就是低级的抽象依赖高级的抽象,高级的不依赖低级的抽象)
class MyHighLevelClass {
MyLowLevelClass dep = new MyLowLeverClass();
}
class App {
void main() { new HighLevelClass().doStuff(); }
}
在这里,MyHighLevelClass
无法访问无法编译MyLowLevelClass
。要打破这种耦合,我们需要使用接口抽象低级类,并删除直接实例化。
class MyLowLevelClass implements MyUsefulAbstraction { ... }
class MyHighLevelClass {
MyUsefulAbstraction dep;
MyHighLevelClass( MyUsefulAbstraction dep ) {
this.dep = dep;
}
}
class App {
void main() { new HighLevelClass( new LowLevelClass() ).doStuff(); }
}
请注意,您不需要任何特殊的容器来强制执行依赖项反转,这是一个原则。叫The Dependency Inversion Principle 。
3.依赖注入
现在是依赖注入。对我来说dependency injection = IoC + dependency inversion
:
- 外部提供依赖项,因此我们强制执行依赖项反转原则
- 容器设置依赖关系(不是我们)所以我们谈论控制的反转
在上面提供的示例中,如果使用容器实例化对象并在构造函数中自动注入依赖项(我们经常说DI容器),则可以执行依赖注入:
class App {
void main() { DI.getHighLevelObject().doStuff(); }
}
请注意,有各种形式的注射。还要注意,在这种观点下,setter注入可以看作是一种回调形式 - DI容器创建对象然后回调setter。控制流有效地反转。
4. AOP
严格来说,AOP与之前的3点没什么关系。关于AOP的开创性论文非常通用,并提出了将各种来源编织在一起(可能用不同语言表达)以生成工作软件的想法。
我不会在AOP上进一步扩展。这里重要的是,依赖注入和AOP可以很好地协同工作,因为它使编织变得非常容易。如果使用IoC容器和依赖注入来抽象出对象的实例化,则可以在注入依赖项之前轻松地使用IoC容器编织方面。否则,这将需要特殊编译或特殊编辑ClassLoader
。