控制反转

控制反转(英語:Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,计算机程序的客户编写部分从外部来源(比如框架)接收控制流程。某些应用框架具有采用了依赖注入的控制反转容器[1]。
概述
[编辑]IoC可以理解为把流程的控制从应用程序转移到框架之中。以前,应用程序掌握整个处理流程;现在,控制权转移到了框架,框架利用一个引擎驱动整个流程的执行,框架会以相应的形式提供一系列的扩展点,应用程序则通过定义扩展的方式实现对流程某个环节的定制,“框架Call应用”。基于MVC的web应用程序就是如此。
使用
[编辑]- Mesa编程环境用于XDE,1985年[2]。
- Visual Basic,1991年。
- HTML DOM事件。
- Spring Framework[3]。
- ASP.NET Core[4]。
- 模板方法模式。
样例代码
[编辑]应用框架
[编辑]下面是一个Java的框架基础设施,它针对插入命令PluginCommand,定义了应用引擎的建造者Builder的生命周期,和应用引擎ApplicationEngine的执行循环run():
import java.util.ArrayList;
import java.util.List;
interface PluginCommand {
void execute();
}
class ApplicationEngine {
private final List<PluginCommand> plugins;
private ApplicationEngine(Builder builder) {
this.plugins = new ArrayList<>(builder.plugins);
}
public void run() {
for (PluginCommand plugin : plugins) {
plugin.execute();
}
}
public static class Builder {
private final List<PluginCommand> plugins = new ArrayList<>();
public Builder registerPlugin(PluginCommand plugin) {
this.plugins.add(plugin);
return this;
}
public ApplicationEngine build() {
return new ApplicationEngine(this);
}
}
}
下面是被控制的应用代码本身:
class DatabaseBackupPlugin implements PluginCommand {
@Override
public void execute() {
System.out.println(" -> [Custom Code] DatabaseBackupPlugin: Exporting SQL dump...");
}
}
class EmailNotificationPlugin implements PluginCommand {
@Override
public void execute() {
System.out.println(" -> [Custom Code] EmailNotificationPlugin: Sending system status email...");
}
}
接着如下这样运行:
ApplicationEngine engine = new ApplicationEngine.Builder()
.registerPlugin(new DatabaseBackupPlugin())
.registerPlugin(new EmailNotificationPlugin())
.build();
engine.run();
HTML DOM事件
[编辑]Web浏览器为HTML中的DOM事件实现了控制反转。应用开发者使用document.addEventListener()来注册回调函数:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>DOM Level 2</title>
</head>
<body>
<h1>DOM Level 2 Event handler</h1>
<div id="output"></div>
<script>
var listener = function () {
document.getElementById("output").innerHTML = "<p>The registered listener was called.</p>";
}
document.addEventListener("click", listener, true);
document.getElementById("output").innerHTML = "<p>The event handler has been registered. If you click the page, your web browser will call the event handler.</p>"
</script>
</body>
</html>
Web应用框架
[编辑]下面的代码例子是ASP.NET Core的C# web应用,它创建一个web应用主机,注册一个端点,并接着传递控制至这个框架[4]:
using Microsoft.AspNetCore.Builder;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
WebApplication app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
控制反转容器
[编辑]使用Java語言寫成的程式,在控制反轉容器(Inversion of Control Container)裡應用了控制反轉[1],軟體需要一個來自容器的物件,而容器自行建構物件和它的附屬物,这样可以减低计算机代码之间的耦合度。
实现控制反转容器主要有两种方式:依赖注入和服务定位器。两者的区别在于,前者是被动的接收对象,在类A的实例创建过程中即创建了依赖的B对象,通过类型或名称来判断将不同的对象注入到不同的属性中,而后者使用了一个中央注册表(registry),它在收到查找请求的时候返回进行特定任务所必须的信息,据此调用框架提供的方法来获取所需对象。
早在2004年,Martin Fowler就提出了“哪些方面的控制被反转了?”这个问题。他总结出是依赖对象的获得被反转了,因为大多数应用程序都是由两个或是更多的类通过彼此的合作来实现业务逻辑,这使得每个对象都需要获取与其合作的对象(也就是它所依赖的对象)的引用。如果这个获取过程要靠自身实现,那么这将导致代码高度耦合并且难以维护和调试。
例如:Class A中用到了Class B的对象b,一般情况下,需要在A的代码中显式地用new建立B的对象。采用依赖注入技术之后,A 的代码只需要定义一个private的B对象,不需要直接new来获得这个对象,而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如XML)来指定。
C++
[编辑]- PocoCapsule[5] LGPL开源的,支持完全非侵入C++的控制反转(IoC)及领域特定建模(DSM)容器
- hypodermic[6] MIT开源协议,Hypodermic是一个基于C++11开发的控制反转(IoC)容器,它为你的C++对象协作提供依赖注入。Hypodermic灵感来自著名的.NET IoC项目Autofac。
Java
[编辑]ATG的Dynamo應用程式伺服器是第一個利用控制反轉容器的環境之一,近來關於這些容器的例子包含了HiveMind、PicoContainer[7]、Spring Framework(注意Spring是一個完整的企業平台,而非IOC容器)、Apache Excalibur、Seasar和DPML Metro[8]。
.NET
[编辑]PHP
[编辑]参见
[编辑]引用
[编辑]- ^ 1.0 1.1 Martin Fowler. Inversion of Control Containers and the Dependency Injection pattern. 2004.
- ^ Sweet, Richard. The Mesa Programming Environment
. ACM SIGPLAN Notices. 25 June 1985, 20 (7): 216–229. doi:10.1145/17919.806843.
- ^ Spring Framework The IoC container. docs.spring.io. [25 May 2023].
- ^ 4.0 4.1 Ryan Nowak, Kirk Larkin, Rick Anderson. "Routing in ASP.Net Core". learn.microsoft.com. microsoft. [25 May 2023].
Routing is responsible for matching incoming HTTP requests and dispatching those requests to the app's executable endpoints. Endpoints are the app's units of executable request-handling code. Endpoints are defined in the app and configured when the app starts.
- ^ PocoCapsule IoC and DSM framework (页面存档备份,存于互联网档案馆)
- ^ hypodermic (页面存档备份,存于互联网档案馆)
- ^ PicoContainer (页面存档备份,存于互联网档案馆)
- ^ DPML Metro
- ^ Ninject is a lightning-fast, ultra-lightweight dependency injector for .NET applications.
- ^ Spring.NET (页面存档备份,存于互联网档案馆)
- ^ ObjectBuilder (页面存档备份,存于互联网档案馆)
- ^ Structuremap — A Dependency Injection/Inversion of Control tool for .NET.
- ^ CastleProject
- ^ Seasar (页面存档备份,存于互联网档案馆)
- ^ Autofac (页面存档备份,存于互联网档案馆)
- ^ Unity (页面存档备份,存于互联网档案馆)
- ^ DI in Phalcon (页面存档备份,存于互联网档案馆)
- ^ Service Container